Handle module import failures and duplicates safely and add module_loaded
Build / AerScript (push) Successful in 39s
Build / AerScript (push) Successful in 39s
This commit is contained in:
+77
-23
@@ -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);
|
||||
break;
|
||||
}
|
||||
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 },
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user