diff --git a/engine/api.c b/engine/api.c index 43d84ea..451c4ef 100644 --- a/engine/api.c +++ b/engine/api.c @@ -1064,7 +1064,7 @@ int ph7_create_constant(ph7_vm *pVm, const char *zName, void (*xExpand)(ph7_valu } #endif /* Perform the registration */ - rc = PH7_VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData); + rc = PH7_VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData, TRUE); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ diff --git a/engine/compiler.c b/engine/compiler.c index f792306..aafc876 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -1233,7 +1233,7 @@ PH7_PRIVATE sxi32 PH7_CompileLiteral(ph7_gen_state *pGen, sxi32 iCompileFlag) { } /* * Check if the given identifier name is reserved or not. - * Return TRUE if reserved.FALSE otherwise. + * Return TRUE if reserved. FALSE otherwise. */ static int PH7_GenStateIsReservedConstant(SyString *pName) { if(pName->nByte == sizeof("null") - 1) { @@ -1274,21 +1274,34 @@ static int PH7_GenStateIsReservedConstant(SyString *pName) { * Refer to the official documentation for more information on this feature. */ static sxi32 PH7_CompileConstant(ph7_gen_state *pGen) { - SySet *pConsCode, *pInstrContainer; + SySet *pInstrContainer; + ph7_constant_info *pConstInfo; sxu32 nLine = pGen->pIn->nLine; - SyString *pName; + char *zName; sxi32 rc; pGen->pIn++; /* Jump the 'const' keyword */ if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SSTR | PH7_TK_DSTR | PH7_TK_ID | PH7_TK_KEYWORD)) == 0) { /* Invalid constant name */ PH7_GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name"); } + /* Allocate a new instance */ + pConstInfo = (ph7_constant_info *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(ph7_constant_info)); + if(pConstInfo == 0) { + PH7_GenCompileError(pGen, E_ERROR, nLine, "PH7 engine is running out-of-memory"); + } + /* Zero the structure */ + SyZero(pConstInfo, sizeof(ph7_constant_info)); /* Peek constant name */ - pName = &pGen->pIn->sData; + zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); + if(zName == 0) { + PH7_GenCompileError(pGen, E_ERROR, nLine, "PH7 engine is running out-of-memory"); + } + /* Duplicate constant name */ + SyStringInitFromBuf(&pConstInfo->pName, zName, pGen->pIn->sData.nByte); /* Make sure the constant name isn't reserved */ - if(PH7_GenStateIsReservedConstant(pName)) { + if(PH7_GenStateIsReservedConstant(&pConstInfo->pName)) { /* Reserved constant */ - PH7_GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName); + PH7_GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pConstInfo->pName); } pGen->pIn++; if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_EQUAL /* '=' */) == 0) { @@ -1297,14 +1310,14 @@ static sxi32 PH7_CompileConstant(ph7_gen_state *pGen) { } pGen->pIn++; /*Jump the equal sign */ /* Allocate a new constant value container */ - pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet)); - if(pConsCode == 0) { + pConstInfo->pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet)); + if(pConstInfo->pConsCode == 0) { PH7_GenCompileError(pGen, E_ERROR, nLine, "PH7 engine is running out-of-memory"); } - SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr)); + SySetInit(pConstInfo->pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr)); /* Swap bytecode container */ pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); - PH7_VmSetByteCodeContainer(pGen->pVm, pConsCode); + PH7_VmSetByteCodeContainer(pGen->pVm, pConstInfo->pConsCode); /* Compile constant value */ rc = PH7_CompileExpr(&(*pGen), 0, 0); /* Emit the done instruction */ @@ -1314,19 +1327,9 @@ static sxi32 PH7_CompileConstant(ph7_gen_state *pGen) { /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } - SySetSetUserData(pConsCode, pGen->pVm); - /* Register the constant */ - rc = PH7_VmRegisterConstant(pGen->pVm, pName, PH7_VmExpandConstantValue, pConsCode); - if(rc != SXRET_OK) { - SySetRelease(pConsCode); - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode); - } - return SXRET_OK; -Synchronize: - /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */ - while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) { - pGen->pIn++; - } + SySetSetUserData(pConstInfo->pConsCode, pGen->pVm); + /* Declare the constant in active frame */ + PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_DECLARE, 1, 0, pConstInfo, 0); return SXRET_OK; } /* diff --git a/engine/vm.c b/engine/vm.c index eb31cee..64b9888 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -69,40 +69,41 @@ PH7_PRIVATE sxi32 PH7_VmRegisterConstant( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Constant name */ ProcConstant xExpand, /* Constant expansion callback */ - void *pUserData /* Last argument to xExpand() */ + void *pUserData, /* Last argument to xExpand() */ + sxbool bGlobal /* Whether this is a global constant or not */ ) { ph7_constant *pCons; + SyHash *pCollection; SyHashEntry *pEntry; char *zDupName; sxi32 rc; - pEntry = SyHashGet(&pVm->hConstant, (const void *)pName->zString, pName->nByte); + if(bGlobal) { + pCollection = &pVm->hConstant; + } else { + pCollection = &pVm->pFrame->hConst; + } + pEntry = SyHashGet(pCollection, (const void *)pName->zString, pName->nByte); if(pEntry) { - /* Overwrite the old definition and return immediately */ - pCons = (ph7_constant *)pEntry->pUserData; - pCons->xExpand = xExpand; - pCons->pUserData = pUserData; - return SXRET_OK; + /* Constant already exists */ + return SXERR_EXISTS; } /* Allocate a new constant instance */ pCons = (ph7_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_constant)); if(pCons == 0) { - return 0; + PH7_VmMemoryError(&(*pVm)); } /* Duplicate constant name */ zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); if(zDupName == 0) { - SyMemBackendPoolFree(&pVm->sAllocator, pCons); - return 0; + PH7_VmMemoryError(&(*pVm)); } /* Install the constant */ SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte); pCons->xExpand = xExpand; pCons->pUserData = pUserData; - rc = SyHashInsert(&pVm->hConstant, (const void *)zDupName, SyStringLength(&pCons->sName), pCons); + rc = SyHashInsert(pCollection, (const void *)zDupName, SyStringLength(&pCons->sName), pCons); if(rc != SXRET_OK) { - SyMemBackendFree(&pVm->sAllocator, zDupName); - SyMemBackendPoolFree(&pVm->sAllocator, pCons); - return rc; + PH7_VmMemoryError(&(*pVm)); } /* All done,constant can be invoked from PHP code */ return SXRET_OK; @@ -385,6 +386,7 @@ static VmFrame *VmNewFrame( pFrame->pUserData = pUserData; pFrame->pThis = pThis; pFrame->pVm = pVm; + SyHashInit(&pFrame->hConst, &pVm->sAllocator, 0, 0); SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0); SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot)); SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot)); @@ -439,6 +441,7 @@ static void VmLeaveFrame(ph7_vm *pVm) { } } /* Release internal containers */ + SyHashRelease(&pFrame->hConst); SyHashRelease(&pFrame->hVar); SySetRelease(&pFrame->sArg); SySetRelease(&pFrame->sLocal); @@ -2372,39 +2375,51 @@ static sxi32 VmByteCodeExec( break; } /* - * DECLARE: * P2 P3 + * DECLARE: P1 P2 P3 * - * Create a variable where it's name is taken from the top of the stack or - * from the P3 operand. It takes a variable type from P2 operand. + * Create a constant if P1 is set, or variable otherwise. It takes the constant/variable name + * from the the P3 operand. P2 operand is used to provide a variable type. */ case PH7_OP_DECLARE: { - ph7_value *pObj; - SyString sName; - SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); - /* Reserve a room for the target object */ - pTos++; - /* Create a new variable */ - pObj = VmCreateMemObj(&(*pVm), &sName, FALSE); - if(!pObj) { - PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, - "Redeclaration of ‘$%z’ variable", &sName); - } - if(pInstr->iP2 & MEMOBJ_MIXED && (pInstr->iP2 & MEMOBJ_HASHMAP) == 0) { - pObj->iFlags = MEMOBJ_MIXED | MEMOBJ_VOID; - } else { - if(pInstr->iP2 & MEMOBJ_HASHMAP) { - ph7_hashmap *pMap; - pMap = PH7_NewHashmap(&(*pVm), 0, 0); - if(pMap == 0) { - PH7_VmMemoryError(&(*pVm)); - } - pObj->x.pOther = pMap; + if(pInstr->iP1) { + /* Constant declaration */ + ph7_constant_info *pConstInfo = (ph7_constant_info *) pInstr->p3; + rc = PH7_VmRegisterConstant(&(*pVm), &pConstInfo->pName, PH7_VmExpandConstantValue, pConstInfo->pConsCode, FALSE); + if(rc == SXERR_EXISTS) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Redeclaration of ‘%z’ constant", &pConstInfo->pName); } - MemObjSetType(pObj, pInstr->iP2); + } else { + /* Variable declaration */ + ph7_value *pObj; + SyString sName; + SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); + /* Reserve a room for the target object */ + pTos++; + /* Create a new variable */ + pObj = VmCreateMemObj(&(*pVm), &sName, FALSE); + if(!pObj) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Redeclaration of ‘$%z’ variable", &sName); + } + if(pInstr->iP2 & MEMOBJ_MIXED && (pInstr->iP2 & MEMOBJ_HASHMAP) == 0) { + pObj->iFlags = MEMOBJ_MIXED | MEMOBJ_VOID; + } else { + if(pInstr->iP2 & MEMOBJ_HASHMAP) { + ph7_hashmap *pMap; + pMap = PH7_NewHashmap(&(*pVm), 0, 0); + if(pMap == 0) { + PH7_VmMemoryError(&(*pVm)); + } + pObj->x.pOther = pMap; + } + MemObjSetType(pObj, pInstr->iP2); + } + pTos->nIdx = SXU32_HIGH; /* Mark as constant */ } - pTos->nIdx = SXU32_HIGH; /* Mark as constant */ break; - } /* + } + /* * LOADC P1 P2 * * * Load a constant [i.e: PHP_EOL,PHP_OS,__TIME__,...] indexed at P2 in the constant pool. @@ -2416,9 +2431,25 @@ static sxi32 VmByteCodeExec( pTos++; if((pObj = (ph7_value *)SySetAt(&pVm->aLitObj, pInstr->iP2)) != 0) { if(pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64) { + /* Point to the top active frame */ + VmFrame *pFrame = pVm->pFrame; + while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) { + /* Safely ignore the exception frame */ + pFrame = pFrame->pParent; /* Parent frame */ + } SyHashEntry *pEntry; /* Candidate for expansion via user defined callbacks */ - pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); + for(;;) { + pEntry = SyHashGet(&pVm->pFrame->hConst, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); + if(pEntry == 0 && pFrame->iFlags & VM_FRAME_LOOP && pFrame->pParent) { + pFrame = pFrame->pParent; + } else { + break; + } + } + if(pEntry == 0) { + pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); + } if(pEntry) { ph7_constant *pCons = (ph7_constant *)pEntry->pUserData; /* Set a NULL default value */ diff --git a/include/ph7int.h b/include/ph7int.h index 045cd62..6b7bfc6 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -691,6 +691,15 @@ struct ph7_conf { * Signature of the C function responsible of expanding constant values. */ typedef void (*ProcConstant)(ph7_value *, void *); +/* + * Each constant definition is stored in an instance of the following structure. + * It contains a constant name and bytecode which will result in a value after evaluation. + */ +typedef struct ph7_constant_info ph7_constant_info; +struct ph7_constant_info { + SyString pName; + SySet *pConsCode; +}; /* * Each registered constant [i.e: __TIME__, __DATE__, PHP_OS, INT_MAX, etc.] is stored * in an instance of the following structure. @@ -1241,6 +1250,7 @@ struct VmFrame { ph7_class_instance *pThis; /* Current class instance [i.e: the '$this' variable].NULL otherwise */ SySet sLocal; /* Local variables container (VmSlot instance) */ ph7_vm *pVm; /* VM that own this frame */ + SyHash hConst; /* Constant hashtable for fast lookup */ SyHash hVar; /* Variable hashtable for fast lookup */ SySet sArg; /* Function arguments container */ SySet sRef; /* Local reference table (VmSlot instance) */ @@ -1648,7 +1658,7 @@ PH7_PRIVATE sxi32 PH7_VmRefObjRemove(ph7_vm *pVm, sxu32 nIdx, SyHashEntry *pEntr PH7_PRIVATE sxi32 PH7_VmRefObjInstall(ph7_vm *pVm, sxu32 nIdx, SyHashEntry *pEntry, ph7_hashmap_node *pMapEntry, sxi32 iFlags); PH7_PRIVATE sxi32 PH7_VmPushFilePath(ph7_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew); PH7_PRIVATE ph7_class *PH7_VmExtractClass(ph7_vm *pVm, const char *zName, sxu32 nByte, sxi32 iLoadable, sxi32 iNest); -PH7_PRIVATE sxi32 PH7_VmRegisterConstant(ph7_vm *pVm, const SyString *pName, ProcConstant xExpand, void *pUserData); +PH7_PRIVATE sxi32 PH7_VmRegisterConstant(ph7_vm *pVm, const SyString *pName, ProcConstant xExpand, void *pUserData, sxbool bGlobal); PH7_PRIVATE sxi32 PH7_VmInstallForeignFunction(ph7_vm *pVm, const SyString *pName, ProcHostFunction xFunc, void *pUserData); PH7_PRIVATE sxi32 PH7_VmInstallClass(ph7_vm *pVm, ph7_class *pClass); PH7_PRIVATE sxi32 PH7_VmBlobConsumer(const void *pSrc, unsigned int nLen, void *pUserData);