From 6628a1ea3d5d2bb8cb55a420eec597cef7f25afc Mon Sep 17 00:00:00 2001 From: belliash Date: Wed, 25 Jul 2018 13:34:17 +0200 Subject: [PATCH 01/20] This is probably what we would like to skip during compilation. --- engine/compiler.c | 78 +++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/engine/compiler.c b/engine/compiler.c index 7e04685..696e035 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -4277,29 +4277,29 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { } /* 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) { - rc = PH7_GenCompileError(pGen, E_ERROR, nLine, - "Class '%z' may not inherit from final class '%z'", pName, &pBase->sName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - } - } +// /* 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) { +// rc = PH7_GenCompileError(pGen, E_ERROR, nLine, +// "Class '%z' may not inherit from final class '%z'", pName, &pBase->sName); +// if(rc == SXERR_ABORT) { +// /* Error count limit reached,abort immediately */ +// return SXERR_ABORT; +// } +// } +// } /* Advance the stream cursor */ pGen->pIn++; } @@ -4585,22 +4585,22 @@ 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(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; +// } +// } +// } SySetRelease(&aInterfaces); if(rc != SXRET_OK) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); From 0c08bc61d43d1e1285ef4571628e08ee3990344e Mon Sep 17 00:00:00 2001 From: belliash Date: Wed, 25 Jul 2018 13:41:31 +0200 Subject: [PATCH 02/20] Add some basic tests for the beginning --- tests/test1.php | 9 +++++++++ tests/test2.php | 12 ++++++++++++ tests/test3.php | 16 ++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/test1.php create mode 100644 tests/test2.php create mode 100644 tests/test3.php diff --git a/tests/test1.php b/tests/test1.php new file mode 100644 index 0000000..9cac8eb --- /dev/null +++ b/tests/test1.php @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/tests/test2.php b/tests/test2.php new file mode 100644 index 0000000..b884f6d --- /dev/null +++ b/tests/test2.php @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/tests/test3.php b/tests/test3.php new file mode 100644 index 0000000..f1c70e2 --- /dev/null +++ b/tests/test3.php @@ -0,0 +1,16 @@ +arg; + } + } + +$test = new Test2(); +$test->test(); + +?> \ No newline at end of file From 7bf76c3fa088a44fd33e3e81d62dba6e535a6810 Mon Sep 17 00:00:00 2001 From: belliash Date: Wed, 25 Jul 2018 16:54:25 +0200 Subject: [PATCH 03/20] Make reported errors more PHP-like --- engine/vm.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/engine/vm.c b/engine/vm.c index e7e7e32..41521c2 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -2188,12 +2188,6 @@ PH7_PRIVATE sxi32 PH7_VmThrowError( SyBlobReset(pWorker); /* Peek the processed file if available */ pFile = (SyString *)SySetPeek(&pVm->aFiles); - if(pFile) { - /* Append file name */ - SyBlobAppend(pWorker, pFile->zString, pFile->nByte); - SyBlobAppend(pWorker, (const void *)" ", sizeof(char)); - } - zErr = "Error: "; switch(iErr) { case PH7_CTX_WARNING: zErr = "Warning: "; @@ -2203,17 +2197,23 @@ PH7_PRIVATE sxi32 PH7_VmThrowError( break; default: iErr = PH7_CTX_ERR; + zErr = "Error: "; break; } SyBlobAppend(pWorker, zErr, SyStrlen(zErr)); - if(pFuncName) { - /* Append function name first */ - SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte); - SyBlobAppend(pWorker, "(): ", sizeof("(): ") - 1); - } SyBlobAppend(pWorker, zMessage, SyStrlen(zMessage)); + if(pFile) { + /* Append file name */ + SyBlobAppend(pWorker, " in ", sizeof(" in ") - 1); + SyBlobAppend(pWorker, pFile->zString, pFile->nByte); + } /* Consume the error message */ rc = VmCallErrorHandler(&(*pVm), pWorker); + if(iErr == PH7_CTX_ERR) { + /* Error ocurred, release at least VM gracefully and exit */ + PH7_VmRelease(pVm); + exit(255); + } return rc; } /* From 63b0f450d30d08233bb57109e3ec48a614cf2617 Mon Sep 17 00:00:00 2001 From: belliash Date: Wed, 25 Jul 2018 18:15:40 +0200 Subject: [PATCH 04/20] Move VM initialization into separate step to enable errors reporting earlier. --- engine/api.c | 68 +++++++++++++++++++++++++++++-------------------- engine/vm.c | 4 --- sapi/cli/main.c | 51 ++++++++++++++++++++++--------------- 3 files changed, 70 insertions(+), 53 deletions(-) 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/vm.c b/engine/vm.c index 41521c2..934632b 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -1412,10 +1412,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); 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, From 09451f4042da0dc24a819a2f4a862c6981122100 Mon Sep 17 00:00:00 2001 From: belliash Date: Wed, 25 Jul 2018 19:00:49 +0200 Subject: [PATCH 05/20] Two changes come here: * do not allow two or more classes / interfaces with the same name * fix class_exists() builtin function so that it does not return true if interface is found --- engine/compiler.c | 19 +++++-------------- engine/vm.c | 44 ++++++++++++++++++-------------------------- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/engine/compiler.c b/engine/compiler.c index 696e035..ef32d5d 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -4046,11 +4046,8 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { } 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) { + /* Only interface is allowed */ + if(pBase == 0 || (pBase->iFlags & PH7_CLASS_INTERFACE) == 0) { /* Inexistant interface */ rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base interface '%z'", pBaseName); if(rc == SXERR_ABORT) { @@ -4279,11 +4276,8 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { 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) { +// /* Interface is not allowed */ +// if(pBase == 0 || (pBase->iFlags & PH7_CLASS_INTERFACE)) { // /* Inexistant base class */ // rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base class '%z'", pBaseName); // if(rc == SXERR_ABORT) { @@ -4325,10 +4319,7 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { /* 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) { + if(pInterface == 0 || (pInterface->iFlags & PH7_CLASS_INTERFACE) == 0) { /* Inexistant interface */ rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base interface '%z'", pIntName); if(rc == SXERR_ABORT) { diff --git a/engine/vm.c b/engine/vm.c index 934632b..47c4e95 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; @@ -6762,14 +6757,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); @@ -6801,14 +6803,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; } } } @@ -11677,17 +11674,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 */ From 0e6e6773bb9b20d6d355122d1a57416463c86a4d Mon Sep 17 00:00:00 2001 From: belliash Date: Wed, 25 Jul 2018 19:04:08 +0200 Subject: [PATCH 06/20] This field is useless now --- include/ph7int.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/ph7int.h b/include/ph7int.h index 232a299..1724a22 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -1035,7 +1035,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] */ From c24a9bc2518ae5980d6d8b9f410c2b77525e2d59 Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 08:22:55 +0200 Subject: [PATCH 07/20] Our implementation of SyStrncpy() --- engine/lib/string.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/engine/lib/string.c b/engine/lib/string.c index f714568..d83875c 100644 --- a/engine/lib/string.c +++ b/engine/lib/string.c @@ -325,6 +325,13 @@ sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen) { zBuf[0] = 0; return (sxu32)(zBuf - (unsigned char *)zDest); } +sxi32 SyStrncpy(char *zDest, const char *zSrc, sxu32 nLen) { + sxu32 i = 0; + while(i++ != nLen && (*zDest++ = *zSrc++)); + *zDest = '\0'; + return SXRET_OK; +} + sxi32 SyAsciiToHex(sxi32 c) { if(c >= 'a' && c <= 'f') { c += 10 - 'a'; @@ -339,4 +346,4 @@ sxi32 SyAsciiToHex(sxi32 c) { return c; } return 0; -} \ No newline at end of file +} From b040886b9725a623d8fd268709bde6e03ff67a1b Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 08:24:53 +0200 Subject: [PATCH 08/20] Test and temporary version of compiler emiting PH7_OP_CLASS_INIT instruction. However it works on ph7_class and thus passes whole class into the VM, what causes memory overhead, as finally we have to find this class on the VM's stack. Instead, we could pass some ph7_class_info structure containing a name of class to look for and information about its inheritances. --- engine/compiler.c | 114 ++++++++++++++++++---------------------------- engine/oop.c | 2 + engine/vm.c | 41 +++++++++++++++++ include/ph7int.h | 4 ++ 4 files changed, 91 insertions(+), 70 deletions(-) diff --git a/engine/compiler.c b/engine/compiler.c index ef32d5d..84db7f3 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -4226,6 +4226,8 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { 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) { @@ -4256,50 +4258,42 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { /* 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; + for(;;) { + 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; } - return SXRET_OK; + /* Extract base class name */ + char *pName = malloc(pGen->pIn->sData.nByte); + SyStrncpy(pName, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); + SyStringInitFromBuf(&pBaseName, pName, SyStrlen(pName)); + /* Register inherited class */ + SySetPut(&pClass->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++; } - /* Extract base class name */ - pBaseName = &pGen->pIn->sData; -// /* Perform the query */ -// pBase = PH7_VmExtractClass(pGen->pVm, pBaseName->zString, pBaseName->nByte, FALSE, 0); -// /* Interface is not allowed */ -// if(pBase == 0 || (pBase->iFlags & PH7_CLASS_INTERFACE)) { -// /* 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) { -// rc = PH7_GenCompileError(pGen, E_ERROR, nLine, -// "Class '%z' may not inherit from final class '%z'", pName, &pBase->sName); -// if(rc == SXERR_ABORT) { -// /* Error count limit reached,abort immediately */ -// return SXERR_ABORT; -// } -// } -// } - /* 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(;;) { @@ -4308,35 +4302,28 @@ 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 */ - if(pInterface == 0 || (pInterface->iFlags & PH7_CLASS_INTERFACE) == 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 *pName = malloc(pGen->pIn->sData.nByte); + SyStrncpy(pName, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); + SyStringInitFromBuf(&pIntName, pName, SyStrlen(pName)); + /* Register inherited class */ + SySetPut(&pClass->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) { @@ -4576,23 +4563,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; -// } -// } -// } - SySetRelease(&aInterfaces); + 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, pClass, 0); + } if(rc != SXRET_OK) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); return SXERR_ABORT; diff --git a/engine/oop.c b/engine/oop.c index 33ca357..33c947b 100644 --- a/engine/oop.c +++ b/engine/oop.c @@ -41,6 +41,8 @@ PH7_PRIVATE ph7_class *PH7_NewRawClass(ph7_vm *pVm, const SyString *pName, sxu32 SyHashInit(&pClass->hAttr, &pVm->sAllocator, 0, 0); SyHashInit(&pClass->hDerived, &pVm->sAllocator, 0, 0); SySetInit(&pClass->aInterface, &pVm->sAllocator, sizeof(ph7_class *)); + SySetInit(&pClass->sExtends, &pVm->sAllocator, sizeof(SyString)); + SySetInit(&pClass->sImplements, &pVm->sAllocator, sizeof(SyString)); pClass->nLine = nLine; /* All done */ return pClass; diff --git a/engine/vm.c b/engine/vm.c index 47c4e95..cf3bc8a 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -4508,6 +4508,41 @@ 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 *pClass = (ph7_class *)pInstr->p3; + printf("Called by class: '%s'\n", pClass->sName); + if(pInstr->iP1) { + /* This class inherits from other classes */ + SyString *apExtends; + while(SySetGetNextEntry(&pClass->sExtends, (void **)&apExtends) == SXRET_OK) { + printf("Class '%s' inherits from '%s'\n", pClass->sName.zString, apExtends->zString); + } + } + if(pInstr->iP2) { + /* This class implements some interfaces */ + SyString *apImplements; + while(SySetGetNextEntry(&pClass->sImplements, (void **)&apImplements) == SXRET_OK) { + printf("Class '%s' implements '%s'\n", pClass->sName.zString, apImplements->zString); + } + } + SyStringInitFromBuf(&pClass->sName, "DUPA", 4); + break; + } + /* + * OP_INTERFACE_INIT * * P3 + * Perform additional interface initialization, by adding base interfaces + * to its definition. + */ + case PH7_OP_INTERFACE_INIT: + { + break; + } /* * OP_FOREACH_INIT * P2 P3 * Prepare a foreach step. @@ -6025,6 +6060,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; diff --git a/include/ph7int.h b/include/ph7int.h index 1724a22..cb3e9e2 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -1035,6 +1035,8 @@ struct ph7_class { SyHash hMethod; /* Class methods */ sxu32 nLine; /* Line number on which this class was declared */ SySet aInterface; /* Implemented interface container */ + SySet sExtends; /* List of inherited classes / interfaces */ + SySet sImplements; /* List of implemented interfaces */ }; /* Class configuration flags */ #define PH7_CLASS_FINAL 0x001 /* Class is final [cannot be extended] */ @@ -1360,6 +1362,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 */ From e70a89fcdba0d15b8b620391ac728f5c45c3e6d9 Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 08:28:57 +0200 Subject: [PATCH 09/20] This is useless. --- engine/vm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/vm.c b/engine/vm.c index cf3bc8a..f30a391 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -4531,7 +4531,6 @@ static sxi32 VmByteCodeExec( printf("Class '%s' implements '%s'\n", pClass->sName.zString, apImplements->zString); } } - SyStringInitFromBuf(&pClass->sName, "DUPA", 4); break; } /* From af2c23d3b22fecb9ab7ae79d3684cbc8ed028eb9 Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 17:01:58 +0200 Subject: [PATCH 10/20] Add some basic checks. At this point, I can say it detects correctly a class declared inside inclueded file. --- engine/vm.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/engine/vm.c b/engine/vm.c index f30a391..9adc531 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -4515,20 +4515,41 @@ static sxi32 VmByteCodeExec( */ case PH7_OP_CLASS_INIT: { - ph7_class *pClass = (ph7_class *)pInstr->p3; - printf("Called by class: '%s'\n", pClass->sName); + ph7_class *pClassInfo = (ph7_class *)pInstr->p3; + ph7_class *pBase = 0; + ph7_class *pClass; + printf("Called by class: '%s'\n", pClassInfo->sName); if(pInstr->iP1) { /* This class inherits from other classes */ SyString *apExtends; - while(SySetGetNextEntry(&pClass->sExtends, (void **)&apExtends) == SXRET_OK) { - printf("Class '%s' inherits from '%s'\n", pClass->sName.zString, apExtends->zString); + while(SySetGetNextEntry(&pClassInfo->sExtends, (void **)&apExtends) == SXRET_OK) { + printf("Class '%s' inherits from '%s'\n", pClassInfo->sName.zString, apExtends->zString); + pBase = PH7_VmExtractClass(pVm, apExtends->zString, apExtends->nByte, FALSE, 0); + if(pBase == 0) { + /* Non-existent base class */ + PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Call to non-existent base class"); + } else if(pBase->iFlags & PH7_CLASS_INTERFACE) { + /* Trying to inherit from interface */ + PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Class cannot inherit from interface"); + } else if(pBase->iFlags & PH7_CLASS_FINAL) { + /* Trying to inherit from final class */ + PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Class cannot inherit from final class"); + } } } if(pInstr->iP2) { /* This class implements some interfaces */ SyString *apImplements; - while(SySetGetNextEntry(&pClass->sImplements, (void **)&apImplements) == SXRET_OK) { - printf("Class '%s' implements '%s'\n", pClass->sName.zString, apImplements->zString); + while(SySetGetNextEntry(&pClassInfo->sImplements, (void **)&apImplements) == SXRET_OK) { + printf("Class '%s' implements '%s'\n", pClassInfo->sName.zString, apImplements->zString); + pBase = PH7_VmExtractClass(pVm, apImplements->zString, apImplements->nByte, FALSE, 0); + if(pBase == 0) { + /* Non-existent interface */ + PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Call to non-existent interface"); + } else if((pBase->iFlags & PH7_CLASS_INTERFACE) == 0) { + /* Trying to extend a class */ + PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Interface cannot inherit from a class"); + } } } break; From 9e885b3196a63153c4a9825bc29ef189c4ecedc7 Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 17:42:12 +0200 Subject: [PATCH 11/20] Generally speaking ... this is working. This commit partially fixes #5, however it still needs some work. TODO: * remove debugging printf calls, * some tuning, to consume less memory, * implement similar solution for interfaces --- engine/oop.c | 26 +++++++++++++------------- engine/vm.c | 16 ++++++++++++---- include/ph7int.h | 2 +- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/engine/oop.c b/engine/oop.c index 33c947b..aa0d94f 100644 --- a/engine/oop.c +++ b/engine/oop.c @@ -218,7 +218,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_class *pSub, ph7_class *pBase) { ph7_class_method *pMeth; ph7_class_attr *pAttr; SyHashEntry *pEntry; @@ -239,9 +239,9 @@ PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen, ph7_class *pSub, ph7_cla 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); +// 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); } continue; } @@ -261,20 +261,20 @@ 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); - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } +// 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); +// if(rc == SXERR_ABORT) { +// return SXERR_ABORT; +// } } continue; } 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); +// PH7_GenCompileError(&(*pGen), E_WARNING, pMeth->nLine, +// "Abstract method '%z:%z' must be defined inside child class '%z'", +// &pBase->sName, pName, &pSub->sName); continue; } } diff --git a/engine/vm.c b/engine/vm.c index 9adc531..511d70d 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -4516,14 +4516,14 @@ static sxi32 VmByteCodeExec( case PH7_OP_CLASS_INIT: { ph7_class *pClassInfo = (ph7_class *)pInstr->p3; + ph7_class *pClass = PH7_VmExtractClass(pVm, pClassInfo->sName.zString, pClassInfo->sName.nByte, FALSE, 0); ph7_class *pBase = 0; - ph7_class *pClass; - printf("Called by class: '%s'\n", pClassInfo->sName); + printf("Called by class: '%s'\n", pClass->sName.zString); if(pInstr->iP1) { /* This class inherits from other classes */ SyString *apExtends; while(SySetGetNextEntry(&pClassInfo->sExtends, (void **)&apExtends) == SXRET_OK) { - printf("Class '%s' inherits from '%s'\n", pClassInfo->sName.zString, apExtends->zString); + printf("Class '%s' inherits from '%s'\n", pClass->sName.zString, apExtends->zString); pBase = PH7_VmExtractClass(pVm, apExtends->zString, apExtends->nByte, FALSE, 0); if(pBase == 0) { /* Non-existent base class */ @@ -4535,13 +4535,17 @@ static sxi32 VmByteCodeExec( /* Trying to inherit from final class */ PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Class cannot inherit from final class"); } + rc = PH7_ClassInherit(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) { - printf("Class '%s' implements '%s'\n", pClassInfo->sName.zString, apImplements->zString); + printf("Class '%s' implements '%s'\n", pClass->sName.zString, apImplements->zString); pBase = PH7_VmExtractClass(pVm, apImplements->zString, apImplements->nByte, FALSE, 0); if(pBase == 0) { /* Non-existent interface */ @@ -4550,6 +4554,10 @@ static sxi32 VmByteCodeExec( /* Trying to extend a class */ PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Interface cannot inherit from a class"); } + rc = PH7_ClassImplement(pClass, pBase); + if(rc != SXRET_OK) { + break; + } } } break; diff --git a/include/ph7int.h b/include/ph7int.h index cb3e9e2..215d53f 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -1701,7 +1701,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_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); From 33ab488ace8b3e66924ce34eeb2f775a48f5f0cb Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 18:52:32 +0200 Subject: [PATCH 12/20] Removing a debug printf calls and improve error handling. --- engine/vm.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/engine/vm.c b/engine/vm.c index 511d70d..4324695 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -4518,22 +4518,20 @@ static sxi32 VmByteCodeExec( ph7_class *pClassInfo = (ph7_class *)pInstr->p3; ph7_class *pClass = PH7_VmExtractClass(pVm, pClassInfo->sName.zString, pClassInfo->sName.nByte, FALSE, 0); ph7_class *pBase = 0; - printf("Called by class: '%s'\n", pClass->sName.zString); if(pInstr->iP1) { /* This class inherits from other classes */ SyString *apExtends; while(SySetGetNextEntry(&pClassInfo->sExtends, (void **)&apExtends) == SXRET_OK) { - printf("Class '%s' inherits from '%s'\n", pClass->sName.zString, apExtends->zString); pBase = PH7_VmExtractClass(pVm, apExtends->zString, apExtends->nByte, FALSE, 0); if(pBase == 0) { /* Non-existent base class */ - PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Call to 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 */ - PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Class cannot 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 */ - PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Class cannot 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(pClass, pBase); if(rc != SXRET_OK) { @@ -4545,14 +4543,13 @@ static sxi32 VmByteCodeExec( /* This class implements some interfaces */ SyString *apImplements; while(SySetGetNextEntry(&pClassInfo->sImplements, (void **)&apImplements) == SXRET_OK) { - printf("Class '%s' implements '%s'\n", pClass->sName.zString, apImplements->zString); pBase = PH7_VmExtractClass(pVm, apImplements->zString, apImplements->nByte, FALSE, 0); if(pBase == 0) { /* Non-existent interface */ - PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Call to 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 extend a class */ - PH7_VmThrowError(pVm, 0, PH7_CTX_ERR, "Interface cannot inherit from a class"); + /* 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) { From 4bf46f1a878c3b37ce7c85f75fd5222c6c95a69d Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 19:00:29 +0200 Subject: [PATCH 13/20] I hate typos --- engine/oop.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/oop.c b/engine/oop.c index aa0d94f..9782758 100644 --- a/engine/oop.c +++ b/engine/oop.c @@ -154,7 +154,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; } /* @@ -938,7 +938,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. @@ -1013,7 +1013,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. */ @@ -1087,7 +1087,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 @@ -1103,7 +1103,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)) { From e47eef7d978b8012c6a1485bfeab4e577c439a4b Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 20:01:45 +0200 Subject: [PATCH 14/20] Fix error reporting --- engine/oop.c | 29 ++++++++++++++--------------- engine/vm.c | 6 +++--- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/engine/oop.c b/engine/oop.c index 9782758..478edc2 100644 --- a/engine/oop.c +++ b/engine/oop.c @@ -218,7 +218,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_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; @@ -236,12 +236,12 @@ PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_class *pSub, ph7_class *pBase) { 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; } @@ -261,20 +261,19 @@ PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_class *pSub, ph7_class *pBase) { 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); -// if(rc == SXERR_ABORT) { -// return SXERR_ABORT; -// } + 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; + } } continue; } 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; } } diff --git a/engine/vm.c b/engine/vm.c index 4324695..df1ee90 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -627,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 @@ -2267,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); @@ -4533,7 +4533,7 @@ static sxi32 VmByteCodeExec( /* 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(pClass, pBase); + rc = PH7_ClassInherit(pVm, pClass, pBase); if(rc != SXRET_OK) { break; } From caf9126f0bfb1568a4991d0950c1a55da6242a4d Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 21:05:54 +0200 Subject: [PATCH 15/20] Several changes: * small code cleanup * implement new structure for storing information about class inheritance * implement PH7_NewClassInfo() * make a use of smaller ph7_class_info instead of ph7_class, to reduce memory usage --- engine/compiler.c | 16 ++++++++++------ engine/oop.c | 27 +++++++++++++++++++++++++++ engine/vm.c | 2 +- include/ph7int.h | 14 +++++++++++++- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/engine/compiler.c b/engine/compiler.c index 84db7f3..d5b5e1d 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -4219,9 +4219,9 @@ 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; @@ -4253,8 +4253,12 @@ 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)) { @@ -4280,7 +4284,7 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { SyStrncpy(pName, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); SyStringInitFromBuf(&pBaseName, pName, SyStrlen(pName)); /* Register inherited class */ - SySetPut(&pClass->sExtends, (const void *)&pBaseName); + SySetPut(&pClassInfo->sExtends, (const void *)&pBaseName); /* Advance the stream cursor */ pGen->pIn++; if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_COMMA) == 0) { @@ -4314,7 +4318,7 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { SyStrncpy(pName, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); SyStringInitFromBuf(&pIntName, pName, SyStrlen(pName)); /* Register inherited class */ - SySetPut(&pClass->sImplements, (const void *)&pIntName); + SySetPut(&pClassInfo->sImplements, (const void *)&pIntName); /* Advance the stream cursor */ pGen->pIn++; if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_COMMA) == 0) { @@ -4565,7 +4569,7 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { rc = PH7_VmInstallClass(pGen->pVm, pClass); 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, pClass, 0); + PH7_VmEmitInstr(pGen->pVm, PH7_OP_CLASS_INIT, iP1, iP2, pClassInfo, 0); } if(rc != SXRET_OK) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); diff --git a/engine/oop.c b/engine/oop.c index 478edc2..6762b0f 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. diff --git a/engine/vm.c b/engine/vm.c index df1ee90..f3b1751 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -4515,7 +4515,7 @@ static sxi32 VmByteCodeExec( */ case PH7_OP_CLASS_INIT: { - ph7_class *pClassInfo = (ph7_class *)pInstr->p3; + 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) { diff --git a/include/ph7int.h b/include/ph7int.h index 215d53f..8160e27 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. @@ -1693,6 +1704,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, @@ -1701,7 +1713,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_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); From db93fd8b25c0b4416e6209d9bdd6cb5539877673 Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 21:16:50 +0200 Subject: [PATCH 16/20] Make a use of our internal allocator instead of malloc() --- engine/compiler.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/engine/compiler.c b/engine/compiler.c index d5b5e1d..913c10e 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -4280,9 +4280,8 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { return SXRET_OK; } /* Extract base class name */ - char *pName = malloc(pGen->pIn->sData.nByte); - SyStrncpy(pName, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); - SyStringInitFromBuf(&pBaseName, pName, SyStrlen(pName)); + 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 */ @@ -4296,7 +4295,6 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { 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; /* Interface implementation */ pGen->pIn++; /* Advance the stream cursor */ @@ -4314,9 +4312,8 @@ static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { return SXRET_OK; } /* Extract interface name */ - char *pName = malloc(pGen->pIn->sData.nByte); - SyStrncpy(pName, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); - SyStringInitFromBuf(&pIntName, pName, SyStrlen(pName)); + 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 */ From a6a43b5f3bcc99857826820e16a178b0652bcb44 Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 21:19:59 +0200 Subject: [PATCH 17/20] This is useless now --- engine/lib/string.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/engine/lib/string.c b/engine/lib/string.c index d83875c..c1ac7f5 100644 --- a/engine/lib/string.c +++ b/engine/lib/string.c @@ -325,13 +325,6 @@ sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen) { zBuf[0] = 0; return (sxu32)(zBuf - (unsigned char *)zDest); } -sxi32 SyStrncpy(char *zDest, const char *zSrc, sxu32 nLen) { - sxu32 i = 0; - while(i++ != nLen && (*zDest++ = *zSrc++)); - *zDest = '\0'; - return SXRET_OK; -} - sxi32 SyAsciiToHex(sxi32 c) { if(c >= 'a' && c <= 'f') { c += 10 - 'a'; From bdf053a2050430eb5ab960325cc7c5a7760c5d7f Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 23:28:34 +0200 Subject: [PATCH 18/20] Implement a fix also for interface. This commit also adds initial and partial support for multiple inheritance. --- engine/compiler.c | 75 +++++++++++++++++++++++++++++++---------------- engine/vm.c | 23 ++++++++++++++- 2 files changed, 71 insertions(+), 27 deletions(-) diff --git a/engine/compiler.c b/engine/compiler.c index 913c10e..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,41 +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 interface is allowed */ - if(pBase == 0 || (pBase->iFlags & PH7_CLASS_INTERFACE) == 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) { @@ -4188,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"); diff --git a/engine/vm.c b/engine/vm.c index f3b1751..13ef129 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -4560,12 +4560,33 @@ static sxi32 VmByteCodeExec( break; } /* - * OP_INTERFACE_INIT * * P3 + * 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; } /* From 72f2cc2c1bb4e5786033da4ea87f136daeeb3ea9 Mon Sep 17 00:00:00 2001 From: belliash Date: Fri, 27 Jul 2018 23:41:51 +0200 Subject: [PATCH 19/20] Remove useless fields, free some memory. --- engine/oop.c | 2 -- include/ph7int.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/engine/oop.c b/engine/oop.c index 6762b0f..fe079a4 100644 --- a/engine/oop.c +++ b/engine/oop.c @@ -68,8 +68,6 @@ PH7_PRIVATE ph7_class *PH7_NewRawClass(ph7_vm *pVm, const SyString *pName, sxu32 SyHashInit(&pClass->hAttr, &pVm->sAllocator, 0, 0); SyHashInit(&pClass->hDerived, &pVm->sAllocator, 0, 0); SySetInit(&pClass->aInterface, &pVm->sAllocator, sizeof(ph7_class *)); - SySetInit(&pClass->sExtends, &pVm->sAllocator, sizeof(SyString)); - SySetInit(&pClass->sImplements, &pVm->sAllocator, sizeof(SyString)); pClass->nLine = nLine; /* All done */ return pClass; diff --git a/include/ph7int.h b/include/ph7int.h index 8160e27..2cda1c5 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -1046,8 +1046,6 @@ struct ph7_class { SyHash hMethod; /* Class methods */ sxu32 nLine; /* Line number on which this class was declared */ SySet aInterface; /* Implemented interface container */ - SySet sExtends; /* List of inherited classes / interfaces */ - SySet sImplements; /* List of implemented interfaces */ }; /* Class configuration flags */ #define PH7_CLASS_FINAL 0x001 /* Class is final [cannot be extended] */ From 765ec4d074a26ae8fabd0c3b9b817117ef00075e Mon Sep 17 00:00:00 2001 From: belliash Date: Sat, 28 Jul 2018 00:02:48 +0200 Subject: [PATCH 20/20] This is not needed anymore --- tests/test1.php | 9 --------- tests/test2.php | 12 ------------ tests/test3.php | 16 ---------------- 3 files changed, 37 deletions(-) delete mode 100644 tests/test1.php delete mode 100644 tests/test2.php delete mode 100644 tests/test3.php diff --git a/tests/test1.php b/tests/test1.php deleted file mode 100644 index 9cac8eb..0000000 --- a/tests/test1.php +++ /dev/null @@ -1,9 +0,0 @@ - \ No newline at end of file diff --git a/tests/test2.php b/tests/test2.php deleted file mode 100644 index b884f6d..0000000 --- a/tests/test2.php +++ /dev/null @@ -1,12 +0,0 @@ - \ No newline at end of file diff --git a/tests/test3.php b/tests/test3.php deleted file mode 100644 index f1c70e2..0000000 --- a/tests/test3.php +++ /dev/null @@ -1,16 +0,0 @@ -arg; - } - } - -$test = new Test2(); -$test->test(); - -?> \ No newline at end of file