Handle module import failures and duplicates safely and add module_loaded
Build / AerScript (push) Successful in 39s

This commit is contained in:
2026-10-24 22:51:09 +02:00
parent d7fc8c1137
commit b513add46b
2 changed files with 78 additions and 23 deletions
+76 -22
View File
@@ -1503,6 +1503,7 @@ PH7_PRIVATE sxi32 PH7_VmInit(
SySetInit(&pVm->aException, &pVm->sAllocator, sizeof(ph7_exception *));
/* Configuration containers */
SySetInit(&pVm->aModules, &pVm->sAllocator, sizeof(VmModule));
SySetInit(&pVm->aModuleHandles, &pVm->sAllocator, sizeof(VmModule));
SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString));
SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString));
SySetInit(&pVm->aIncluded, &pVm->sAllocator, sizeof(SyString));
@@ -1717,7 +1718,7 @@ PH7_PRIVATE sxi32 PH7_VmReset(ph7_vm *pVm) {
PH7_PRIVATE sxi32 PH7_VmRelease(ph7_vm *pVm) {
VmModule *pEntry;
/* Iterate through modules list */
while(SySetGetNextEntry(&pVm->aModules, (void **)&pEntry) == SXRET_OK) {
while(SySetGetNextEntry(&pVm->aModuleHandles, (void **)&pEntry) == SXRET_OK) {
/* Unload the module */
#ifdef __WINNT__
FreeLibrary(pEntry->pHandle);
@@ -1727,6 +1728,7 @@ PH7_PRIVATE sxi32 PH7_VmRelease(ph7_vm *pVm) {
}
/* Free up the heap */
SySetRelease(&pVm->aModules);
SySetRelease(&pVm->aModuleHandles);
/* Set the stale magic number */
pVm->nMagic = PH7_VM_STALE;
/* Release the private memory subsystem */
@@ -2409,6 +2411,18 @@ PH7_PRIVATE sxi32 PH7_VmThrowError(
}
return rc;
}
/* Exact module-name lookup; do not disturb the set's iteration cursor. */
static sxi32 VmModuleLoaded(ph7_vm *pVm, const char *zName, sxu32 nLen) {
sxu32 i;
VmModule *pModule;
for(i = 0; i < SySetUsed(&pVm->aModules); ++i) {
pModule = (VmModule *)SySetAt(&pVm->aModules, i);
if(pModule->sName.nByte == nLen && SyStrncmp(pModule->sName.zString, zName, nLen) == 0) {
return TRUE;
}
}
return FALSE;
}
/*
* Execute as much of a PH7 bytecode program as we can then return.
*
@@ -4473,51 +4487,78 @@ static sxi32 VmByteCodeExec(
*/
case PH7_OP_IMPORT:
{
VmModule pModule, *pSearch;
VmModule pModule;
char bfile[255];
char *zFile;
int nFile;
sxu32 nSize;
sxi32 (*init)(ph7_vm *, ph7_real *, SyString *);
char *zModule = (char *) pInstr->p3;
int nLen = SyStrlen(zModule);
if(nLen < 1) {
break;
}
while(SySetGetNextEntry(&pVm->aModules, (void **)&pSearch) == SXRET_OK) {
if(SyStrncmp(pSearch->sName.zString, zModule, (sxu32)(SXMAX((int) pSearch->sName.nByte, nLen))) == 0) {
SySetResetCursor(&pVm->aModules);
if(VmModuleLoaded(pVm, zModule, (sxu32)nLen)) {
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: Module '%s' already loaded", zModule);
break;
}
}
SySetResetCursor(&pVm->aModules);
/* Zero the module entry */
SyZero(&pModule, sizeof(VmModule));
SyStringInitFromBuf(&pModule.sName, zModule, nLen);
char bfile[255] = {0};
char *file;
snprintf(bfile, sizeof(bfile) - 1, "./binary/%s%s", zModule, PH7_LIBRARY_SUFFIX);
file = bfile;
SyStringInitFromBuf(&pModule.sFile, file, nLen);
nFile = snprintf(bfile, sizeof(bfile), "./binary/%s%s", zModule, PH7_LIBRARY_SUFFIX);
if(nFile < 0 || nFile >= (int)sizeof(bfile)) {
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: Module path too long for '%s'", zModule);
break;
}
zFile = SyMemBackendStrDup(&pVm->sAllocator, bfile, (sxu32)nFile);
if(zFile == 0) {
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: Out of memory loading '%s'", zModule);
break;
}
SyStringInitFromBuf(&pModule.sFile, zFile, nFile);
#ifdef __WINNT__
pModule.pHandle = LoadLibrary(file);
pModule.pHandle = LoadLibrary(zFile);
#else
pModule.pHandle = dlopen(pModule.sFile.zString, RTLD_LAZY);
#endif
if(!pModule.pHandle) {
/* Could not load the module library file */
PH7_VmThrowError(pVm, PH7_CTX_ERR, "ImportError: No module named '%z' found", &pModule.sName);
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: No module named '%z' found", &pModule.sName);
SyMemBackendFree(&pVm->sAllocator, zFile);
break;
}
void (*init)(ph7_vm *, ph7_real *, SyString *);
#ifdef __WINNT__
*(void**)(&init) = GetProcAddress(pModule.pHandle, "initializeModule");
#else
*(void**)(&init) = dlsym(pModule.pHandle, "initializeModule");
#endif
if(!init) {
/* Could not find the module entry point */
PH7_VmThrowError(pVm, PH7_CTX_ERR, "ImportError: Method '%z::initializeModule()' not found", &pModule.sName);
nSize = pVm->aModuleHandles.nSize;
if(!init || SySetPut(&pVm->aModuleHandles, &pModule) != SXRET_OK) {
/* SySetPut may change capacity before allocation fails. */
pVm->aModuleHandles.nSize = nSize;
/* No callbacks have been published yet; unloading is safe. */
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: Cannot initialize module '%z'", &pModule.sName);
#ifdef __WINNT__
FreeLibrary(pModule.pHandle);
#else
dlclose(pModule.pHandle);
#endif
SyMemBackendFree(&pVm->sAllocator, zFile);
break;
}
/* Init may publish callbacks even on failure. Retain its handle until
* VM release, but only successful imports enter the lookup registry. */
if(init(pVm, &pModule.fVer, &pModule.sDesc) != SXRET_OK) {
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: Initialization of module '%z' failed", &pModule.sName);
} else {
nSize = pVm->aModules.nSize;
if(SySetPut(&pVm->aModules, &pModule) != SXRET_OK) {
/* Keep the lookup registry retryable after allocation failure. */
pVm->aModules.nSize = nSize;
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: Cannot register module '%z'", &pModule.sName);
}
}
/* Initialize the module */
init(pVm, &pModule.fVer, &pModule.sDesc);
/* Put information about module on top of the modules stack */
SySetPut(&pVm->aModules, (const void *)&pModule);
break;
}
/*
@@ -5825,6 +5866,18 @@ PH7_PRIVATE void PH7_VmExpandConstantValue(ph7_value *pVal, void *pUserData) {
* Status:
* Stable.
*/
/* bool module_loaded(string $name): lookup only, without loading a library. */
static int vm_builtin_module_loaded(ph7_context *pCtx, int nArg, ph7_value **apArg) {
const char *zName;
int nLen;
if(nArg < 1) {
ph7_result_bool(pCtx, FALSE);
return SXRET_OK;
}
zName = ph7_value_to_string(apArg[0], &nLen);
ph7_result_bool(pCtx, VmModuleLoaded(pCtx->pVm, zName, (sxu32)nLen));
return SXRET_OK;
}
/*
* bool function_exists(string $name)
* Return TRUE if the given function has been defined.
@@ -10054,6 +10107,7 @@ static int vm_builtin_utf8_decode(ph7_context *pCtx, int nArg, ph7_value **apArg
}
/* Table of built-in VM functions. */
static const ph7_builtin_func aVmFunc[] = {
{ "module_loaded", vm_builtin_module_loaded },
{ "function_exists", vm_builtin_func_exists },
{ "is_callable", vm_builtin_is_callable },
{ "register_autoload_handler", vm_builtin_register_autoload_handler },
+1
View File
@@ -1204,6 +1204,7 @@ struct ph7_vm {
SyBlob sConsumer; /* Default VM consumer [i.e Redirect all VM output to this blob] */
SyBlob sArgv; /* $argv[] collector [refer to the [getopt()] implementation for more information] */
SySet aModules; /* Set of loaded modules */
SySet aModuleHandles; /* Libraries retained after initialization attempts */
SySet aFiles; /* Stack of processed files */
SySet aPaths; /* Set of import paths */
SySet aIncluded; /* Set of included files */