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 *)); SySetInit(&pVm->aException, &pVm->sAllocator, sizeof(ph7_exception *));
/* Configuration containers */ /* Configuration containers */
SySetInit(&pVm->aModules, &pVm->sAllocator, sizeof(VmModule)); SySetInit(&pVm->aModules, &pVm->sAllocator, sizeof(VmModule));
SySetInit(&pVm->aModuleHandles, &pVm->sAllocator, sizeof(VmModule));
SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString)); SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString));
SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString)); SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString));
SySetInit(&pVm->aIncluded, &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) { PH7_PRIVATE sxi32 PH7_VmRelease(ph7_vm *pVm) {
VmModule *pEntry; VmModule *pEntry;
/* Iterate through modules list */ /* Iterate through modules list */
while(SySetGetNextEntry(&pVm->aModules, (void **)&pEntry) == SXRET_OK) { while(SySetGetNextEntry(&pVm->aModuleHandles, (void **)&pEntry) == SXRET_OK) {
/* Unload the module */ /* Unload the module */
#ifdef __WINNT__ #ifdef __WINNT__
FreeLibrary(pEntry->pHandle); FreeLibrary(pEntry->pHandle);
@@ -1727,6 +1728,7 @@ PH7_PRIVATE sxi32 PH7_VmRelease(ph7_vm *pVm) {
} }
/* Free up the heap */ /* Free up the heap */
SySetRelease(&pVm->aModules); SySetRelease(&pVm->aModules);
SySetRelease(&pVm->aModuleHandles);
/* Set the stale magic number */ /* Set the stale magic number */
pVm->nMagic = PH7_VM_STALE; pVm->nMagic = PH7_VM_STALE;
/* Release the private memory subsystem */ /* Release the private memory subsystem */
@@ -2409,6 +2411,18 @@ PH7_PRIVATE sxi32 PH7_VmThrowError(
} }
return rc; 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. * Execute as much of a PH7 bytecode program as we can then return.
* *
@@ -4473,51 +4487,78 @@ static sxi32 VmByteCodeExec(
*/ */
case PH7_OP_IMPORT: 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; char *zModule = (char *) pInstr->p3;
int nLen = SyStrlen(zModule); int nLen = SyStrlen(zModule);
if(nLen < 1) { if(nLen < 1) {
break; break;
} }
while(SySetGetNextEntry(&pVm->aModules, (void **)&pSearch) == SXRET_OK) { if(VmModuleLoaded(pVm, zModule, (sxu32)nLen)) {
if(SyStrncmp(pSearch->sName.zString, zModule, (sxu32)(SXMAX((int) pSearch->sName.nByte, nLen))) == 0) { PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: Module '%s' already loaded", zModule);
SySetResetCursor(&pVm->aModules);
break; break;
} }
}
SySetResetCursor(&pVm->aModules);
/* Zero the module entry */ /* Zero the module entry */
SyZero(&pModule, sizeof(VmModule)); SyZero(&pModule, sizeof(VmModule));
SyStringInitFromBuf(&pModule.sName, zModule, nLen); SyStringInitFromBuf(&pModule.sName, zModule, nLen);
char bfile[255] = {0}; nFile = snprintf(bfile, sizeof(bfile), "./binary/%s%s", zModule, PH7_LIBRARY_SUFFIX);
char *file; if(nFile < 0 || nFile >= (int)sizeof(bfile)) {
snprintf(bfile, sizeof(bfile) - 1, "./binary/%s%s", zModule, PH7_LIBRARY_SUFFIX); PH7_VmThrowError(pVm, PH7_CTX_WARNING, "ImportError: Module path too long for '%s'", zModule);
file = bfile; break;
SyStringInitFromBuf(&pModule.sFile, file, nLen); }
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__ #ifdef __WINNT__
pModule.pHandle = LoadLibrary(file); pModule.pHandle = LoadLibrary(zFile);
#else #else
pModule.pHandle = dlopen(pModule.sFile.zString, RTLD_LAZY); pModule.pHandle = dlopen(pModule.sFile.zString, RTLD_LAZY);
#endif #endif
if(!pModule.pHandle) { if(!pModule.pHandle) {
/* Could not load the module library file */ /* 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__ #ifdef __WINNT__
*(void**)(&init) = GetProcAddress(pModule.pHandle, "initializeModule"); *(void**)(&init) = GetProcAddress(pModule.pHandle, "initializeModule");
#else #else
*(void**)(&init) = dlsym(pModule.pHandle, "initializeModule"); *(void**)(&init) = dlsym(pModule.pHandle, "initializeModule");
#endif #endif
if(!init) { nSize = pVm->aModuleHandles.nSize;
/* Could not find the module entry point */ if(!init || SySetPut(&pVm->aModuleHandles, &pModule) != SXRET_OK) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "ImportError: Method '%z::initializeModule()' not found", &pModule.sName); /* 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; break;
} }
/* /*
@@ -5825,6 +5866,18 @@ PH7_PRIVATE void PH7_VmExpandConstantValue(ph7_value *pVal, void *pUserData) {
* Status: * Status:
* Stable. * 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) * bool function_exists(string $name)
* Return TRUE if the given function has been defined. * 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. */ /* Table of built-in VM functions. */
static const ph7_builtin_func aVmFunc[] = { static const ph7_builtin_func aVmFunc[] = {
{ "module_loaded", vm_builtin_module_loaded },
{ "function_exists", vm_builtin_func_exists }, { "function_exists", vm_builtin_func_exists },
{ "is_callable", vm_builtin_is_callable }, { "is_callable", vm_builtin_is_callable },
{ "register_autoload_handler", vm_builtin_register_autoload_handler }, { "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 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] */ SyBlob sArgv; /* $argv[] collector [refer to the [getopt()] implementation for more information] */
SySet aModules; /* Set of loaded modules */ SySet aModules; /* Set of loaded modules */
SySet aModuleHandles; /* Libraries retained after initialization attempts */
SySet aFiles; /* Stack of processed files */ SySet aFiles; /* Stack of processed files */
SySet aPaths; /* Set of import paths */ SySet aPaths; /* Set of import paths */
SySet aIncluded; /* Set of included files */ SySet aIncluded; /* Set of included files */