diff --git a/engine/api.c b/engine/api.c index 2abe686..346c202 100644 --- a/engine/api.c +++ b/engine/api.c @@ -604,6 +604,44 @@ int ph7_release(ph7 *pEngine) { SyMemBackendPoolFree(&sMPGlobal.sAllocator, pEngine); return rc; } +int ph7_vm_init( + ph7 *pEngine, /* Running PH7 engine */ + ph7_vm **ppOutVm /* OUT: A pointer to the virtual machine */ +) { + ph7_vm *pVm; + int rc; + if(ppOutVm) { + *ppOutVm = 0; + } + /* Allocate a new virtual machine */ + pVm = (ph7_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator, sizeof(ph7_vm)); + if(pVm == 0) { + /* If the supplied memory subsystem is so sick that we are unable to allocate + * a tiny chunk of memory, there is no much we can do here. */ + if(ppOutVm) { + *ppOutVm = 0; + } + return PH7_NOMEM; + } + /* Initialize the Virtual Machine */ + rc = PH7_VmInit(pVm, &(*pEngine)); + if(rc != PH7_OK) { + SyMemBackendPoolFree(&pEngine->sAllocator, pVm); + if(ppOutVm) { + *ppOutVm = 0; + } + return PH7_VM_ERR; + } + /* Reset the error message consumer */ + SyBlobReset(&pEngine->xConf.sErrConsumer); + /* Set the default VM output consumer callback and it's + * private data. */ + pVm->sVmConsumer.xConsumer = PH7_VmBlobConsumer; + pVm->sVmConsumer.pUserData = &pVm->sConsumer; + /* Point to the freshly created VM */ + *ppOutVm = pVm; + return PH7_OK; +} /* * Compile a raw PHP script. * To execute a PHP code, it must first be compiled into a byte-code program using this routine. @@ -621,33 +659,14 @@ static sxi32 ProcessScript( sxi32 iFlags, /* Compile-time flags */ const char *zFilePath /* File path if script come from a file. NULL otherwise */ ) { - ph7_vm *pVm; + ph7_vm *pVm = *ppVm; int iFileDir, rc; char *pFileDir, *fFilePath[PATH_MAX + 1]; char *pFilePath[PATH_MAX + 1]; - /* Allocate a new virtual machine */ - pVm = (ph7_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator, sizeof(ph7_vm)); - if(pVm == 0) { - /* If the supplied memory subsystem is so sick that we are unable to allocate - * a tiny chunk of memory, there is no much we can do here. */ - if(ppVm) { - *ppVm = 0; - } - return PH7_NOMEM; - } if(iFlags < 0) { /* Default compile-time flags */ iFlags = 0; } - /* Initialize the Virtual Machine */ - rc = PH7_VmInit(pVm, &(*pEngine)); - if(rc != PH7_OK) { - SyMemBackendPoolFree(&pEngine->sAllocator, pVm); - if(ppVm) { - *ppVm = 0; - } - return PH7_VM_ERR; - } /* Install local import path which is the current directory */ ph7_vm_config(pVm, PH7_VM_CONFIG_IMPORT_PATH, "./"); if(zFilePath && SyRealPath(zFilePath, fFilePath) == PH7_OK) { @@ -660,8 +679,6 @@ static sxi32 ProcessScript( /* Push processed file path */ PH7_VmPushFilePath(pVm, pFilePath, -1, TRUE, 0); } - /* Reset the error message consumer */ - SyBlobReset(&pEngine->xConf.sErrConsumer); /* Compile the script */ PH7_CompileScript(pVm, &(*pScript), iFlags); if(pVm->sCodeGen.nErr > 0 || pVm == 0) { @@ -691,8 +708,6 @@ static sxi32 ProcessScript( /* Script successfully compiled,link to the list of active virtual machines */ MACRO_LD_PUSH(pEngine->pVms, pVm); pEngine->iVm++; - /* Point to the freshly created VM */ - *ppVm = pVm; /* Ready to execute PH7 bytecode */ return PH7_OK; Release: @@ -772,9 +787,6 @@ int ph7_compile_v2(ph7 *pEngine, const char *zSource, int nLen, ph7_vm **ppOutVm int ph7_compile_file(ph7 *pEngine, const char *zFilePath, ph7_vm **ppOutVm, int iFlags) { const ph7_vfs *pVfs; int rc; - if(ppOutVm) { - *ppOutVm = 0; - } rc = PH7_OK; /* cc warning */ if(PH7_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath)) { return PH7_CORRUPT; @@ -859,7 +871,7 @@ int ph7_vm_config(ph7_vm *pVm, int iConfigOp, ...) { return PH7_ABORT; /* Another thread have released this instance */ } #endif - /* Confiugure the virtual machine */ + /* Configure the virtual machine */ va_start(ap, iConfigOp); rc = PH7_VmConfigure(&(*pVm), iConfigOp, ap); va_end(ap); diff --git a/engine/compiler.c b/engine/compiler.c index 7e04685..b9b3e54 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -4006,12 +4006,27 @@ Synchronize: static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { sxu32 nLine = pGen->pIn->nLine; ph7_class *pClass, *pBase; + ph7_class_info *pClassInfo; SyToken *pEnd, *pTmp; SyString *pName; sxi32 nKwrd; sxi32 rc; + sxi32 iP1 = 0; /* Jump the 'interface' keyword */ pGen->pIn++; + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) { + /* Syntax error */ + rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Invalid interface name"); + if(rc == SXERR_ABORT) { + /* Error count limit reached,abort immediately */ + return SXERR_ABORT; + } + /* Synchronize with the first semi-colon or curly braces */ + while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_OCB/*'{'*/ | PH7_TK_SEMI/*';'*/)) == 0) { + pGen->pIn++; + } + return SXRET_OK; + } /* Extract interface name */ pName = &pGen->pIn->sData; /* Advance the stream cursor */ @@ -4022,44 +4037,49 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); return SXERR_ABORT; } + /* Obtain a raw class inheritance storage */ + pClassInfo = PH7_NewClassInfo(pGen->pVm, pName); + if(pClassInfo == 0) { + PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); + return SXERR_ABORT; + } /* Mark as an interface */ pClass->iFlags = PH7_CLASS_INTERFACE; /* Assume no base class is given */ pBase = 0; if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) { + SyString pBaseName; nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if(nKwrd == PH7_TKWRD_EXTENDS /* interface b extends a */) { - SyString *pBaseName; /* Extract base interface */ pGen->pIn++; - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) { - /* Syntax error */ - rc = PH7_GenCompileError(pGen, E_ERROR, nLine, - "Expected 'interface_name' after 'extends' keyword inside interface '%z'", - pName); - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; + for(;;) { + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) { + /* Syntax error */ + rc = PH7_GenCompileError(pGen, E_ERROR, nLine, + "Expected 'interface_name' after 'extends' keyword inside interface '%z'", + pName); + SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass); + if(rc == SXERR_ABORT) { + /* Error count limit reached,abort immediately */ + return SXERR_ABORT; + } + return SXRET_OK; } - return SXRET_OK; - } - pBaseName = &pGen->pIn->sData; - pBase = PH7_VmExtractClass(pGen->pVm, pBaseName->zString, pBaseName->nByte, FALSE, 0); - /* Only interfaces is allowed */ - while(pBase && (pBase->iFlags & PH7_CLASS_INTERFACE) == 0) { - pBase = pBase->pNextName; - } - if(pBase == 0) { - /* Inexistant interface */ - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base interface '%z'", pBaseName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; + /* Extract base class name */ + char *sName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); + SyStringInitFromBuf(&pBaseName, sName, SyStrlen(sName)); + /* Register inherited class */ + SySetPut(&pClassInfo->sExtends, (const void *)&pBaseName); + /* Advance the stream cursor */ + pGen->pIn++; + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_COMMA) == 0) { + break; } + /* Jump the comma operator */ + pGen->pIn++; } - /* Advance the stream cursor */ - pGen->pIn++; + iP1 = 1; } } if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_OCB /*'{'*/) == 0) { @@ -4191,9 +4211,9 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { } /* Install the interface */ rc = PH7_VmInstallClass(pGen->pVm, pClass); - if(rc == SXRET_OK && pBase) { - /* Inherit from the base interface */ - rc = PH7_ClassInterfaceInherit(pClass, pBase); + if(iP1) { + /* Emit the INTERFACE_INIT instruction only if there is such a need */ + PH7_VmEmitInstr(pGen->pVm, PH7_OP_INTERFACE_INIT, iP1, 0, pClassInfo, 0); } if(rc != SXRET_OK) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); @@ -4222,13 +4242,15 @@ done: static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { sxu32 nLine = pGen->pIn->nLine; ph7_class *pClass, *pBase; + ph7_class_info *pClassInfo; SyToken *pEnd, *pTmp; sxi32 iProtection; - SySet aInterfaces; sxi32 iAttrflags; SyString *pName; sxi32 nKwrd; sxi32 rc; + sxi32 iP1 = 0; + sxu32 iP2 = 0; /* Jump the 'class' keyword */ pGen->pIn++; if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) { @@ -4254,58 +4276,49 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); return SXERR_ABORT; } - /* implemented interfaces container */ - SySetInit(&aInterfaces, &pGen->pVm->sAllocator, sizeof(ph7_class *)); + /* Obtain a raw class inheritance storage */ + pClassInfo = PH7_NewClassInfo(pGen->pVm, pName); + if(pClassInfo == 0) { + PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); + return SXERR_ABORT; + } /* Assume a standalone class */ pBase = 0; if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) { - SyString *pBaseName; + SyString pBaseName; nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if(nKwrd == PH7_TKWRD_EXTENDS /* class b extends a */) { pGen->pIn++; /* Advance the stream cursor */ - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) { - /* Syntax error */ - rc = PH7_GenCompileError(pGen, E_ERROR, nLine, - "Expected 'class_name' after 'extends' keyword inside class '%z'", - pName); - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - return SXRET_OK; - } - /* Extract base class name */ - pBaseName = &pGen->pIn->sData; - /* Perform the query */ - pBase = PH7_VmExtractClass(pGen->pVm, pBaseName->zString, pBaseName->nByte, FALSE, 0); - /* Interfaces are not allowed */ - while(pBase && (pBase->iFlags & PH7_CLASS_INTERFACE)) { - pBase = pBase->pNextName; - } - if(pBase == 0) { - /* Inexistant base class */ - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base class '%z'", pBaseName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - } else { - if(pBase->iFlags & PH7_CLASS_FINAL) { + for(;;) { + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) { + /* Syntax error */ rc = PH7_GenCompileError(pGen, E_ERROR, nLine, - "Class '%z' may not inherit from final class '%z'", pName, &pBase->sName); + "Expected 'class_name' after 'extends' keyword inside class '%z'", + pName); + SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass); if(rc == SXERR_ABORT) { /* Error count limit reached,abort immediately */ return SXERR_ABORT; } + return SXRET_OK; } + /* Extract base class name */ + char *sName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); + SyStringInitFromBuf(&pBaseName, sName, SyStrlen(sName)); + /* Register inherited class */ + SySetPut(&pClassInfo->sExtends, (const void *)&pBaseName); + /* Advance the stream cursor */ + pGen->pIn++; + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_COMMA) == 0) { + break; + } + /* Jump the comma operator */ + pGen->pIn++; } - /* Advance the stream cursor */ - pGen->pIn++; + iP1 = 1; } if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_IMPLEMENTS) { - ph7_class *pInterface; - SyString *pIntName; + SyString pIntName; /* Interface implementation */ pGen->pIn++; /* Advance the stream cursor */ for(;;) { @@ -4314,38 +4327,27 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected 'interface_name' after 'implements' keyword inside class '%z' declaration", pName); + SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass); if(rc == SXERR_ABORT) { /* Error count limit reached,abort immediately */ return SXERR_ABORT; } - break; + return SXRET_OK; } /* Extract interface name */ - pIntName = &pGen->pIn->sData; - /* Make sure the interface is already defined */ - pInterface = PH7_VmExtractClass(pGen->pVm, pIntName->zString, pIntName->nByte, FALSE, 0); - /* Only interfaces are allowed */ - while(pInterface && (pInterface->iFlags & PH7_CLASS_INTERFACE) == 0) { - pInterface = pInterface->pNextName; - } - if(pInterface == 0) { - /* Inexistant interface */ - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base interface '%z'", pIntName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - } else { - /* Register interface */ - SySetPut(&aInterfaces, (const void *)&pInterface); - } + char *sName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); + SyStringInitFromBuf(&pIntName, sName, SyStrlen(sName)); + /* Register inherited class */ + SySetPut(&pClassInfo->sImplements, (const void *)&pIntName); /* Advance the stream cursor */ pGen->pIn++; if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_COMMA) == 0) { break; } - pGen->pIn++;/* Jump the comma */ + /* Jump the comma */ + pGen->pIn++; } + iP2 = 1; } } if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_OCB /*'{'*/) == 0) { @@ -4585,23 +4587,10 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { } /* Install the class */ rc = PH7_VmInstallClass(pGen->pVm, pClass); - if(rc == SXRET_OK) { - ph7_class **apInterface; - sxu32 n; - if(pBase) { - /* Inherit from base class and mark as a subclass */ - rc = PH7_ClassInherit(&(*pGen), pClass, pBase); - } - apInterface = (ph7_class **)SySetBasePtr(&aInterfaces); - for(n = 0 ; n < SySetUsed(&aInterfaces) ; n++) { - /* Implements one or more interface */ - rc = PH7_ClassImplement(pClass, apInterface[n]); - if(rc != SXRET_OK) { - break; - } - } + if(iP1 || iP2) { + /* Emit the CLASS_INIT instruction only if there is such a need */ + PH7_VmEmitInstr(pGen->pVm, PH7_OP_CLASS_INIT, iP1, iP2, pClassInfo, 0); } - SySetRelease(&aInterfaces); if(rc != SXRET_OK) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); return SXERR_ABORT; diff --git a/engine/lib/string.c b/engine/lib/string.c index f714568..c1ac7f5 100644 --- a/engine/lib/string.c +++ b/engine/lib/string.c @@ -339,4 +339,4 @@ sxi32 SyAsciiToHex(sxi32 c) { return c; } return 0; -} \ No newline at end of file +} diff --git a/engine/oop.c b/engine/oop.c index 33ca357..fe079a4 100644 --- a/engine/oop.c +++ b/engine/oop.c @@ -15,6 +15,33 @@ /* * This file implement an Object Oriented (OO) subsystem for the PH7 engine. */ +/* + * Create an empty class inheritance storage. + * Return a pointer to a storage (ph7_class_info instance) on success. NULL otherwise. + */ +PH7_PRIVATE ph7_class_info *PH7_NewClassInfo(ph7_vm *pVm, const SyString *pName) { + ph7_class_info *pClassInfo; + char *zName; + /* Allocate a new instance */ + pClassInfo = (ph7_class_info *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_class_info)); + if(pClassInfo == 0) { + return 0; + } + /* Zero the structure */ + SyZero(pClassInfo, sizeof(ph7_class_info)); + /* Duplicate class name */ + zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); + if(zName == 0) { + SyMemBackendPoolFree(&pVm->sAllocator, pClassInfo); + return 0; + } + /* Initialize the class information storage */ + SyStringInitFromBuf(&pClassInfo->sName, zName, pName->nByte); + SySetInit(&pClassInfo->sExtends, &pVm->sAllocator, sizeof(SyString)); + SySetInit(&pClassInfo->sImplements, &pVm->sAllocator, sizeof(SyString)); + /* All done */ + return pClassInfo; +} /* * Create an empty class. * Return a pointer to a raw class (ph7_class instance) on success. NULL otherwise. @@ -152,7 +179,7 @@ PH7_PRIVATE ph7_class_attr *PH7_ClassExtractAttribute(ph7_class *pClass, const c /* No such entry */ return 0; } - /* Point to the desierd method */ + /* Point to the desired method */ return (ph7_class_attr *)pEntry->pUserData; } /* @@ -216,7 +243,7 @@ PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass, ph7_class_method *pM * Any other return value indicates failure and the upper layer must generate an appropriate * error message. */ -PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen, ph7_class *pSub, ph7_class *pBase) { +PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_vm *pVm, ph7_class *pSub, ph7_class *pBase) { ph7_class_method *pMeth; ph7_class_attr *pAttr; SyHashEntry *pEntry; @@ -234,12 +261,12 @@ PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen, ph7_class *pSub, ph7_cla pAttr = (ph7_class_attr *)pEntry->pUserData; pName = &pAttr->sName; if((pEntry = SyHashGet(&pSub->hAttr, (const void *)pName->zString, pName->nByte)) != 0) { - if(pAttr->iProtection == PH7_CLASS_PROT_PRIVATE && - ((ph7_class_attr *)pEntry->pUserData)->iProtection != PH7_CLASS_PROT_PUBLIC) { + if(pAttr->iProtection == PH7_CLASS_PROT_PRIVATE && ((ph7_class_attr *)pEntry->pUserData)->iProtection != PH7_CLASS_PROT_PUBLIC) { /* Cannot redeclare private attribute */ - PH7_GenCompileError(&(*pGen), E_WARNING, ((ph7_class_attr *)pEntry->pUserData)->nLine, - "Private attribute '%z::%z' redeclared inside child class '%z'", - &pBase->sName, pName, &pSub->sName); + rc = VmErrorFormat(pVm, PH7_CTX_ERR, "Private attribute '%z::%z' redeclared inside child class '%z'", &pBase->sName, pName, &pSub->sName); + if(rc == SXERR_ABORT) { + return SXERR_ABORT; + } } continue; } @@ -259,9 +286,7 @@ PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen, ph7_class *pSub, ph7_cla if((pEntry = SyHashGet(&pSub->hMethod, (const void *)pName->zString, pName->nByte)) != 0) { if(pMeth->iFlags & PH7_CLASS_ATTR_FINAL) { /* Cannot Overwrite final method */ - rc = PH7_GenCompileError(&(*pGen), E_ERROR, ((ph7_class_method *)pEntry->pUserData)->nLine, - "Cannot Overwrite final method '%z:%z' inside child class '%z'", - &pBase->sName, pName, &pSub->sName); + rc = VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Cannot overwrite final method '%z:%z()' inside child class '%z'", &pBase->sName, pName, &pSub->sName); if(rc == SXERR_ABORT) { return SXERR_ABORT; } @@ -270,9 +295,10 @@ PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen, ph7_class *pSub, ph7_cla } else { if(pMeth->iFlags & PH7_CLASS_ATTR_ABSTRACT) { /* Abstract method must be defined in the child class */ - PH7_GenCompileError(&(*pGen), E_WARNING, pMeth->nLine, - "Abstract method '%z:%z' must be defined inside child class '%z'", - &pBase->sName, pName, &pSub->sName); + rc = VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Abstract method '%z:%z()' must be defined inside child class '%z'", &pBase->sName, pName, &pSub->sName); + if(rc == SXERR_ABORT) { + return SXERR_ABORT; + } continue; } } @@ -936,7 +962,7 @@ PH7_PRIVATE sxi32 PH7_ClassInstanceDump(SyBlob *pOut, ph7_class_instance *pThis, * The above example will output: * Hello * - * Note that PH7 does not support all the magical method and introudces __toFloat(),__toInt() + * Note that PH7 does not support all the magical method and introduces __toFloat(),__toInt() * which have the same behaviour as __toString() but for float and integer types * respectively. * Refer to the official documentation for more information. @@ -1011,7 +1037,7 @@ PH7_PRIVATE ph7_value *PH7_ClassInstanceExtractAttrValue(ph7_class_instance *pTh * int(991) * } * You have noticed that PH7 allow class attributes [i.e: $a,$c,$d in the example above] - * have any complex expression (even function calls/Annonymous functions) as their default + * have any complex expression (even function calls/anonymous functions) as their default * value unlike the standard PHP engine. * This is a very powerful feature that you have to look at. */ @@ -1085,7 +1111,7 @@ PH7_PRIVATE sxi32 PH7_ClassInstanceWalk( return SXRET_OK; } /* - * Extract a class atrribute value. + * Extract a class attribute value. * Return a pointer to the attribute value on success. Otherwise NULL. * Note: * Access to static and constant attribute is not allowed. That is,the function @@ -1101,7 +1127,7 @@ PH7_PRIVATE ph7_value *PH7_ClassInstanceFetchAttr(ph7_class_instance *pThis, con /* No such attribute */ return 0; } - /* Point to the class atrribute */ + /* Point to the class attribute */ pAttr = (VmClassAttr *)pEntry->pUserData; /* Check if we are dealing with a static/constant attribute */ if(pAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) { diff --git a/engine/vm.c b/engine/vm.c index 41521c2..13ef129 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -384,13 +384,8 @@ PH7_PRIVATE sxi32 PH7_VmInstallClass( /* Check for duplicates */ pEntry = SyHashGet(&pVm->hClass, (const void *)pName->zString, pName->nByte); if(pEntry) { - ph7_class *pLink = (ph7_class *)pEntry->pUserData; - /* Link entry with the same name */ - pClass->pNextName = pLink; - pEntry->pUserData = pClass; - return SXRET_OK; + PH7_VmThrowError(&(*pVm), 0, PH7_CTX_ERR, "Cannot declare class, because the name is already in use"); } - pClass->pNextName = 0; /* Perform a simple hashtable insertion */ rc = SyHashInsert(&pVm->hClass, (const void *)pName->zString, pName->nByte, pClass); return rc; @@ -632,7 +627,7 @@ static int VmOverloadCompare(SyString *pFirst, SyString *pSecond) { } /* Forward declaration */ static sxi32 VmLocalExec(ph7_vm *pVm, SySet *pByteCode, ph7_value *pResult); -static sxi32 VmErrorFormat(ph7_vm *pVm, sxi32 iErr, const char *zFormat, ...); +sxi32 VmErrorFormat(ph7_vm *pVm, sxi32 iErr, const char *zFormat, ...); /* * Select the appropriate VM function for the current call context. * This is the implementation of the powerful 'function overloading' feature @@ -1412,10 +1407,6 @@ PH7_PRIVATE sxi32 PH7_VmMakeReady( if(pVm->aOps == 0) { return SXERR_MEM; } - /* Set the default VM output consumer callback and it's - * private data. */ - pVm->sVmConsumer.xConsumer = PH7_VmBlobConsumer; - pVm->sVmConsumer.pUserData = &pVm->sConsumer; /* Allocate the reference table */ pVm->nRefSize = 0x10; /* Must be a power of two for fast arithemtic */ pVm->apRefObj = (VmRefObj **)SyMemBackendAlloc(&pVm->sAllocator, sizeof(VmRefObj *) * pVm->nRefSize); @@ -2276,7 +2267,7 @@ static sxi32 VmThrowErrorAp( * Simple boring wrapper function. * ------------------------------------ */ -static sxi32 VmErrorFormat(ph7_vm *pVm, sxi32 iErr, const char *zFormat, ...) { +sxi32 VmErrorFormat(ph7_vm *pVm, sxi32 iErr, const char *zFormat, ...) { va_list ap; sxi32 rc; va_start(ap, zFormat); @@ -4517,6 +4508,87 @@ static sxi32 VmByteCodeExec( pc = nJump - 1; break; } + /* + * OP_CLASS_INIT P1 P2 P3 + * Perform additional class initialization, by adding base classes + * and interfaces to its definition. + */ + case PH7_OP_CLASS_INIT: + { + ph7_class_info *pClassInfo = (ph7_class_info *)pInstr->p3; + ph7_class *pClass = PH7_VmExtractClass(pVm, pClassInfo->sName.zString, pClassInfo->sName.nByte, FALSE, 0); + ph7_class *pBase = 0; + if(pInstr->iP1) { + /* This class inherits from other classes */ + SyString *apExtends; + while(SySetGetNextEntry(&pClassInfo->sExtends, (void **)&apExtends) == SXRET_OK) { + pBase = PH7_VmExtractClass(pVm, apExtends->zString, apExtends->nByte, FALSE, 0); + if(pBase == 0) { + /* Non-existent base class */ + VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Call to non-existent base class '%z'", &apExtends->zString); + } else if(pBase->iFlags & PH7_CLASS_INTERFACE) { + /* Trying to inherit from interface */ + VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Class '%z' cannot inherit from interface '%z'", &pClass->sName.zString, &apExtends->zString); + } else if(pBase->iFlags & PH7_CLASS_FINAL) { + /* Trying to inherit from final class */ + VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Class '%z' cannot inherit from final class '%z'", &pClass->sName.zString, &apExtends->zString); + } + rc = PH7_ClassInherit(pVm, pClass, pBase); + if(rc != SXRET_OK) { + break; + } + } + } + if(pInstr->iP2) { + /* This class implements some interfaces */ + SyString *apImplements; + while(SySetGetNextEntry(&pClassInfo->sImplements, (void **)&apImplements) == SXRET_OK) { + pBase = PH7_VmExtractClass(pVm, apImplements->zString, apImplements->nByte, FALSE, 0); + if(pBase == 0) { + /* Non-existent interface */ + VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Call to non-existent interface '%z'", &apImplements->zString); + } else if((pBase->iFlags & PH7_CLASS_INTERFACE) == 0) { + /* Trying to implement a class */ + VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Class '%z' cannot implement a class '%z'", &pClass->sName.zString, &apImplements->zString); + } + rc = PH7_ClassImplement(pClass, pBase); + if(rc != SXRET_OK) { + break; + } + } + } + break; + } + /* + * OP_INTERFACE_INIT P1 * P3 + * Perform additional interface initialization, by adding base interfaces + * to its definition. + */ + case PH7_OP_INTERFACE_INIT: + { + ph7_class_info *pClassInfo = (ph7_class_info *)pInstr->p3; + ph7_class *pClass = PH7_VmExtractClass(pVm, pClassInfo->sName.zString, pClassInfo->sName.nByte, FALSE, 0); + ph7_class *pBase = 0; + if(pInstr->iP1) { + /* This interface inherits from other interface */ + SyString *apExtends; + while(SySetGetNextEntry(&pClassInfo->sExtends, (void **)&apExtends) == SXRET_OK) { + pBase = PH7_VmExtractClass(pVm, apExtends->zString, apExtends->nByte, FALSE, 0); + if(pBase == 0) { + /* Non-existent base interface */ + VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Call to non-existent base interface '%z'", &apExtends->zString); + } else if((pBase->iFlags & PH7_CLASS_INTERFACE) == 0) { + /* Trying to inherit from class */ + VmErrorFormat(&(*pVm), PH7_CTX_ERR, "Interface '%z' cannot inherit from class '%z'", &pClass->sName.zString, &apExtends->zString); + } + rc = PH7_ClassInterfaceInherit(pClass, pBase); + if(rc != SXRET_OK) { + break; + } + } + } + break; + } /* * OP_FOREACH_INIT * P2 P3 * Prepare a foreach step. @@ -6034,6 +6106,12 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_THROW: zOp = "THROW "; break; + case PH7_OP_CLASS_INIT: + zOp = "CLASS_INIT "; + break; + case PH7_OP_INTERFACE_INIT: + zOp = "INTER_INIT "; + break; case PH7_OP_FOREACH_INIT: zOp = "4EACH_INIT "; break; @@ -6766,14 +6844,21 @@ static int vm_builtin_method_exists(ph7_context *pCtx, int nArg, ph7_value **apA static int vm_builtin_class_exists(ph7_context *pCtx, int nArg, ph7_value **apArg) { int res = 0; /* Assume class does not exists */ if(nArg > 0) { + SyHashEntry *pEntry = 0; const char *zName; int nLen; /* Extract given name */ zName = ph7_value_to_string(apArg[0], &nLen); /* Perform a hashlookup */ - if(nLen > 0 && SyHashGet(&pCtx->pVm->hClass, (const void *)zName, (sxu32)nLen) != 0) { - /* class is available */ - res = 1; + if(nLen > 0) { + pEntry = SyHashGet(&pCtx->pVm->hClass, (const void *)zName, (sxu32)nLen); + } + if(pEntry) { + ph7_class *pClass = (ph7_class *)pEntry->pUserData; + if((pClass->iFlags & PH7_CLASS_INTERFACE) == 0) { + /* class is available */ + res = 1; + } } } ph7_result_bool(pCtx, res); @@ -6805,14 +6890,9 @@ static int vm_builtin_interface_exists(ph7_context *pCtx, int nArg, ph7_value ** } if(pEntry) { ph7_class *pClass = (ph7_class *)pEntry->pUserData; - while(pClass) { - if(pClass->iFlags & PH7_CLASS_INTERFACE) { - /* interface is available */ - res = 1; - break; - } - /* Next with the same name */ - pClass = pClass->pNextName; + if(pClass->iFlags & PH7_CLASS_INTERFACE) { + /* interface is available */ + res = 1; } } } @@ -11681,17 +11761,12 @@ PH7_PRIVATE ph7_class *PH7_VmExtractClass( } pClass = (ph7_class *)pEntry->pUserData; if(!iLoadable) { - /* Return the first class seen */ + /* Return the class absolutely */ return pClass; } else { - /* Check the collision list */ - while(pClass) { - if((pClass->iFlags & (PH7_CLASS_INTERFACE | PH7_CLASS_ABSTRACT)) == 0) { - /* Class is loadable */ - return pClass; - } - /* Point to the next entry */ - pClass = pClass->pNextName; + if((pClass->iFlags & (PH7_CLASS_INTERFACE | PH7_CLASS_ABSTRACT)) == 0) { + /* Class is loadable */ + return pClass; } } /* No such loadable class */ diff --git a/include/ph7int.h b/include/ph7int.h index 232a299..2cda1c5 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -38,7 +38,9 @@ typedef struct ph7_foreach_info ph7_foreach_info; typedef struct ph7_foreach_step ph7_foreach_step; typedef struct ph7_hashmap_node ph7_hashmap_node; typedef struct ph7_hashmap ph7_hashmap; +typedef struct ph7_class_info ph7_class_info; typedef struct ph7_class ph7_class; + /* Symisc Standard types */ #if !defined(SYMISC_STD_TYPES) #define SYMISC_STD_TYPES @@ -1021,6 +1023,15 @@ struct ph7_builtin_constant { /* Forward reference */ typedef struct ph7_class_method ph7_class_method; typedef struct ph7_class_attr ph7_class_attr; +/* + * Information about class/interface inheritance and interface implementation + * is stored in an instance of the following structure. + */ +struct ph7_class_info { + SyString sName; /* Class full qualified name */ + SySet sExtends; /* List of inherited classes / interfaces */ + SySet sImplements; /* List of implemented interfaces */ +}; /* * Each class is parsed out and stored in an instance of the following structure. * PH7 introduced powerfull extensions to the PHP 5 OO subsystems. @@ -1035,7 +1046,6 @@ struct ph7_class { SyHash hMethod; /* Class methods */ sxu32 nLine; /* Line number on which this class was declared */ SySet aInterface; /* Implemented interface container */ - ph7_class *pNextName; /* Next class [interface, abstract, etc.] with the same name */ }; /* Class configuration flags */ #define PH7_CLASS_FINAL 0x001 /* Class is final [cannot be extended] */ @@ -1361,6 +1371,8 @@ enum ph7_vm_op { PH7_OP_CVT_NULL, /* NULL cast */ PH7_OP_CVT_ARRAY, /* Array cast */ PH7_OP_CVT_OBJ, /* Object cast */ + PH7_OP_CLASS_INIT, /* Class init */ + PH7_OP_INTERFACE_INIT,/* Interface init */ PH7_OP_FOREACH_INIT, /* For each init */ PH7_OP_FOREACH_STEP, /* For each step */ PH7_OP_IS_A, /* Instanceof */ @@ -1690,6 +1702,7 @@ PH7_PRIVATE sxi32 PH7_StripTagsFromString(ph7_context *pCtx, const char *zIn, in PH7_PRIVATE sxi32 PH7_ParseIniString(ph7_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection); #endif /* oo.c function prototypes */ +PH7_PRIVATE ph7_class_info *PH7_NewClassInfo(ph7_vm *pVm, const SyString *pName); PH7_PRIVATE ph7_class *PH7_NewRawClass(ph7_vm *pVm, const SyString *pName, sxu32 nLine); PH7_PRIVATE ph7_class_attr *PH7_NewClassAttr(ph7_vm *pVm, const SyString *pName, sxu32 nLine, sxi32 iProtection, sxi32 iFlags); PH7_PRIVATE ph7_class_method *PH7_NewClassMethod(ph7_vm *pVm, ph7_class *pClass, const SyString *pName, sxu32 nLine, @@ -1698,7 +1711,7 @@ PH7_PRIVATE ph7_class_method *PH7_ClassExtractMethod(ph7_class *pClass, const ch PH7_PRIVATE ph7_class_attr *PH7_ClassExtractAttribute(ph7_class *pClass, const char *zName, sxu32 nByte); PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_class *pClass, ph7_class_attr *pAttr); PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass, ph7_class_method *pMeth); -PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen, ph7_class *pSub, ph7_class *pBase); +PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_vm *pVm, ph7_class *pSub, ph7_class *pBase); PH7_PRIVATE sxi32 PH7_ClassInterfaceInherit(ph7_class *pSub, ph7_class *pBase); PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_class *pMain, ph7_class *pInterface); PH7_PRIVATE ph7_class_instance *PH7_NewClassInstance(ph7_vm *pVm, ph7_class *pClass); diff --git a/sapi/cli/main.c b/sapi/cli/main.c index 7cf879c..45bf05d 100644 --- a/sapi/cli/main.c +++ b/sapi/cli/main.c @@ -152,6 +152,36 @@ int main(int argc, char **argv) { Output_Consumer, /* Error log consumer */ 0 /* NULL: Callback Private data */ ); + /* Initialize the VM */ + rc = ph7_vm_init(pEngine, &pVm); + if(rc != PH7_OK) { + if(rc == PH7_NOMEM) { + Fatal("Out of memory"); + } else if(rc == PH7_VM_ERR) { + Fatal("VM initialization error"); + } + } + /* + * Now we have our VM initialized,it's time to configure our VM. + * We will install the VM output consumer callback defined above + * so that we can consume the VM output and redirect it to STDOUT. + */ + rc = ph7_vm_config(pVm, + PH7_VM_CONFIG_OUTPUT, + Output_Consumer, /* Output Consumer callback */ + 0 /* Callback private data */ + ); + if(rc != PH7_OK) { + Fatal("Error while installing the VM output consumer callback"); + } + rc = ph7_vm_config(pVm, PH7_VM_CONFIG_ERR_REPORT, 1, 0); + if(rc != PH7_OK) { + Fatal("Error while configuring the VM error reporting"); + } + if(err_report) { + /* Report script run-time errors */ + ph7_vm_config(pVm, PH7_VM_CONFIG_ERR_REPORT); + } /* Now,it's time to compile our PHP file */ rc = ph7_compile_file( pEngine, /* PH7 Engine */ @@ -169,33 +199,12 @@ int main(int argc, char **argv) { Fatal("Compile error"); } } - /* - * Now we have our script compiled,it's time to configure our VM. - * We will install the VM output consumer callback defined above - * so that we can consume the VM output and redirect it to STDOUT. - */ - rc = ph7_vm_config(pVm, - PH7_VM_CONFIG_OUTPUT, - Output_Consumer, /* Output Consumer callback */ - 0 /* Callback private data */ - ); - if(rc != PH7_OK) { - Fatal("Error while installing the VM output consumer callback"); - } - rc = ph7_vm_config(pVm, PH7_VM_CONFIG_ERR_REPORT, 1, 0); - if(rc != PH7_OK) { - Fatal("Error while configuring the VM error reporting"); - } /* Register script agruments so we can access them later using the $argv[] * array from the compiled PHP program. */ for(n = n + 1; n < argc ; ++n) { ph7_vm_config(pVm, PH7_VM_CONFIG_ARGV_ENTRY, argv[n]/* Argument value */); } - if(err_report) { - /* Report script run-time errors */ - ph7_vm_config(pVm, PH7_VM_CONFIG_ERR_REPORT); - } if(dump_vm) { /* Dump PH7 byte-code instructions */ ph7_vm_dump_v2(pVm,