/* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: api.c v2.0 FreeBSD 2012-08-18 06:54 stable $ */ #include "ph7int.h" /* This file implement the public interfaces presented to host-applications. * Routines in other files are for internal use by PH7 and should not be * accessed by users of the library. */ #define PH7_ENGINE_MAGIC 0xF874BCD7 #define PH7_ENGINE_MISUSE(ENGINE) (ENGINE == 0 || ENGINE->nMagic != PH7_ENGINE_MAGIC) #define PH7_VM_MISUSE(VM) (VM == 0 || VM->nMagic == PH7_VM_STALE) /* If another thread have released a working instance,the following macros * evaluates to true. These macros are only used when the library * is built with threading support enabled which is not the case in * the default built. */ #define PH7_THRD_ENGINE_RELEASE(ENGINE) (ENGINE->nMagic != PH7_ENGINE_MAGIC) #define PH7_THRD_VM_RELEASE(VM) (VM->nMagic == PH7_VM_STALE) /* IMPLEMENTATION: ph7@embedded@symisc 311-12-32 */ /* * All global variables are collected in the structure named "sMPGlobal". * That way it is clear in the code when we are using static variable because * its name start with sMPGlobal. */ static struct Global_Data { SyMemBackend sAllocator; /* Global low level memory allocator */ #if defined(PH7_ENABLE_THREADS) const SyMutexMethods *pMutexMethods; /* Mutex methods */ SyMutex *pMutex; /* Global mutex */ sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded * The threading level can be set using the [ph7_lib_config()] * interface with a configuration verb set to * PH7_LIB_CONFIG_THREAD_LEVEL_SINGLE or * PH7_LIB_CONFIG_THREAD_LEVEL_MULTI */ #endif const ph7_vfs *pVfs; /* Underlying virtual file system */ sxi32 nEngine; /* Total number of active engines */ ph7 *pEngines; /* List of active engine */ sxu32 nMagic; /* Sanity check against library misuse */ } sMPGlobal = { {0, 0, 0, 0, 0, 0, 0, 0, {0}}, #if defined(PH7_ENABLE_THREADS) 0, 0, 0, #endif 0, 0, 0, 0 }; #define PH7_LIB_MAGIC 0xEA1495BA /* * Supported threading level. * These options have meaning only when the library is compiled with multi-threading * support.That is,the PH7_ENABLE_THREADS compile time directive must be defined * when PH7 is built. * PH7_THREAD_LEVEL_SINGLE: * In this mode,mutexing is disabled and the library can only be used by a single thread. * PH7_THREAD_LEVEL_MULTI * In this mode, all mutexes including the recursive mutexes on [ph7] objects * are enabled so that the application is free to share the same engine * between different threads at the same time. */ #define PH7_THREAD_LEVEL_SINGLE 1 #define PH7_THREAD_LEVEL_MULTI 2 /* * Configure a running PH7 engine instance. * return PH7_OK on success.Any other return * value indicates failure. * Refer to [ph7_config()]. */ static sxi32 EngineConfig(ph7 *pEngine, sxi32 nOp, va_list ap) { ph7_conf *pConf = &pEngine->xConf; int rc = PH7_OK; /* Perform the requested operation */ switch(nOp) { case PH7_CONFIG_ERR_OUTPUT: { ProcConsumer xConsumer = va_arg(ap, ProcConsumer); void *pUserData = va_arg(ap, void *); /* Compile time error consumer routine */ if(xConsumer == 0) { rc = PH7_CORRUPT; break; } /* Install the error consumer */ pConf->xErr = xConsumer; pConf->pErrData = pUserData; break; } case PH7_CONFIG_ERR_LOG: { /* Extract compile-time error log if any */ const char **pzPtr = va_arg(ap, const char **); int *pLen = va_arg(ap, int *); if(pzPtr == 0) { rc = PH7_CORRUPT; break; } /* NULL terminate the error-log buffer */ SyBlobNullAppend(&pConf->sErrConsumer); /* Point to the error-log buffer */ *pzPtr = (const char *)SyBlobData(&pConf->sErrConsumer); if(pLen) { if(SyBlobLength(&pConf->sErrConsumer) > 1 /* NULL '\0' terminator */) { *pLen = (int)SyBlobLength(&pConf->sErrConsumer); } else { *pLen = 0; } } break; } case PH7_CONFIG_ERR_ABORT: /* Reserved for future use */ break; default: /* Unknown configuration verb */ rc = PH7_CORRUPT; break; } /* Switch() */ return rc; } /* * Configure the PH7 library. * return PH7_OK on success.Any other return value * indicates failure. * Refer to [ph7_lib_config()]. */ static sxi32 PH7CoreConfigure(sxi32 nOp, va_list ap) { int rc = PH7_OK; switch(nOp) { case PH7_LIB_CONFIG_VFS: { /* Install a virtual file system */ const ph7_vfs *pVfs = va_arg(ap, const ph7_vfs *); sMPGlobal.pVfs = pVfs; break; } case PH7_LIB_CONFIG_USER_MALLOC: { /* Use an alternative low-level memory allocation routines */ const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *); /* Save the memory failure callback (if available) */ ProcMemError xMemErr = sMPGlobal.sAllocator.xMemError; void *pMemErr = sMPGlobal.sAllocator.pUserData; if(pMethods == 0) { /* Use the built-in memory allocation subsystem */ rc = SyMemBackendInit(&sMPGlobal.sAllocator, xMemErr, pMemErr); } else { rc = SyMemBackendInitFromOthers(&sMPGlobal.sAllocator, pMethods, xMemErr, pMemErr); } break; } case PH7_LIB_CONFIG_MEM_ERR_CALLBACK: { /* Memory failure callback */ ProcMemError xMemErr = va_arg(ap, ProcMemError); void *pUserData = va_arg(ap, void *); sMPGlobal.sAllocator.xMemError = xMemErr; sMPGlobal.sAllocator.pUserData = pUserData; break; } case PH7_LIB_CONFIG_USER_MUTEX: { #if defined(PH7_ENABLE_THREADS) /* Use an alternative low-level mutex subsystem */ const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *); #if defined (UNTRUST) if(pMethods == 0) { rc = PH7_CORRUPT; } #endif /* Sanity check */ if(pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0) { /* At least three criticial callbacks xEnter(),xLeave() and xNew() must be supplied */ rc = PH7_CORRUPT; break; } if(sMPGlobal.pMutexMethods) { /* Overwrite the previous mutex subsystem */ SyMutexRelease(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); if(sMPGlobal.pMutexMethods->xGlobalRelease) { sMPGlobal.pMutexMethods->xGlobalRelease(); } sMPGlobal.pMutex = 0; } /* Initialize and install the new mutex subsystem */ if(pMethods->xGlobalInit) { rc = pMethods->xGlobalInit(); if(rc != PH7_OK) { break; } } /* Create the global mutex */ sMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST); if(sMPGlobal.pMutex == 0) { /* * If the supplied mutex subsystem is so sick that we are unable to * create a single mutex,there is no much we can do here. */ if(pMethods->xGlobalRelease) { pMethods->xGlobalRelease(); } rc = PH7_CORRUPT; break; } sMPGlobal.pMutexMethods = pMethods; if(sMPGlobal.nThreadingLevel == 0) { /* Set a default threading level */ sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_MULTI; } #endif break; } case PH7_LIB_CONFIG_THREAD_LEVEL_SINGLE: #if defined(PH7_ENABLE_THREADS) /* Single thread mode(Only one thread is allowed to play with the library) */ sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_SINGLE; #endif break; case PH7_LIB_CONFIG_THREAD_LEVEL_MULTI: #if defined(PH7_ENABLE_THREADS) /* Multi-threading mode (library is thread safe and PH7 engines and virtual machines * may be shared between multiple threads). */ sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_MULTI; #endif break; default: /* Unknown configuration option */ rc = PH7_CORRUPT; break; } return rc; } /* * [CAPIREF: ph7_lib_config()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_lib_config(int nConfigOp, ...) { va_list ap; int rc; if(sMPGlobal.nMagic == PH7_LIB_MAGIC) { /* Library is already initialized,this operation is forbidden */ return PH7_LOOKED; } va_start(ap, nConfigOp); rc = PH7CoreConfigure(nConfigOp, ap); va_end(ap); return rc; } /* * Global library initialization * Refer to [ph7_lib_init()] * This routine must be called to initialize the memory allocation subsystem,the mutex * subsystem prior to doing any serious work with the library.The first thread to call * this routine does the initialization process and set the magic number so no body later * can re-initialize the library.If subsequent threads call this routine before the first * thread have finished the initialization process, then the subsequent threads must block * until the initialization process is done. */ static sxi32 PH7CoreInitialize(void) { const ph7_vfs *pVfs; /* Built-in vfs */ #if defined(PH7_ENABLE_THREADS) const SyMutexMethods *pMutexMethods = 0; SyMutex *pMaster = 0; #endif int rc; /* * If the library is already initialized,then a call to this routine * is a no-op. */ if(sMPGlobal.nMagic == PH7_LIB_MAGIC) { return PH7_OK; /* Already initialized */ } /* Point to the built-in vfs */ pVfs = PH7_ExportBuiltinVfs(); /* Install it */ ph7_lib_config(PH7_LIB_CONFIG_VFS, pVfs); #if defined(PH7_ENABLE_THREADS) if(sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_SINGLE) { pMutexMethods = sMPGlobal.pMutexMethods; if(pMutexMethods == 0) { /* Use the built-in mutex subsystem */ pMutexMethods = SyMutexExportMethods(); if(pMutexMethods == 0) { return PH7_CORRUPT; /* Can't happen */ } /* Install the mutex subsystem */ rc = ph7_lib_config(PH7_LIB_CONFIG_USER_MUTEX, pMutexMethods); if(rc != PH7_OK) { return rc; } } /* Obtain a static mutex so we can initialize the library without calling malloc() */ pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1); if(pMaster == 0) { return PH7_CORRUPT; /* Can't happen */ } } /* Lock the master mutex */ rc = PH7_OK; SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ if(sMPGlobal.nMagic != PH7_LIB_MAGIC) { #endif if(sMPGlobal.sAllocator.pMethods == 0) { /* Install a memory subsystem */ rc = ph7_lib_config(PH7_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */ if(rc != PH7_OK) { /* If we are unable to initialize the memory backend,there is no much we can do here.*/ goto End; } } #if defined(PH7_ENABLE_THREADS) if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE) { /* Protect the memory allocation subsystem */ rc = SyMemBackendMakeThreadSafe(&sMPGlobal.sAllocator, sMPGlobal.pMutexMethods); if(rc != PH7_OK) { goto End; } } #endif /* Our library is initialized,set the magic number */ sMPGlobal.nMagic = PH7_LIB_MAGIC; rc = PH7_OK; #if defined(PH7_ENABLE_THREADS) } /* sMPGlobal.nMagic != PH7_LIB_MAGIC */ #endif End: #if defined(PH7_ENABLE_THREADS) /* Unlock the master mutex */ SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif return rc; } /* * [CAPIREF: ph7_lib_init()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_lib_init(void) { int rc; rc = PH7CoreInitialize(); return rc; } /* * Release an active PH7 engine and it's associated active virtual machines. */ static sxi32 EngineRelease(ph7 *pEngine) { ph7_vm *pVm, *pNext; /* Release all active VM */ pVm = pEngine->pVms; for(;;) { if(pEngine->iVm <= 0) { break; } pNext = pVm->pNext; PH7_VmRelease(pVm); pVm = pNext; pEngine->iVm--; } /* Set a dummy magic number */ pEngine->nMagic = 0x7635; /* Release the private memory subsystem */ SyMemBackendRelease(&pEngine->sAllocator); return PH7_OK; } /* * Release all resources consumed by the library. * If PH7 is already shut down when this routine * is invoked then this routine is a harmless no-op. * Note: This call is not thread safe. * Refer to [ph7_lib_shutdown()]. */ static void PH7CoreShutdown(void) { ph7 *pEngine, *pNext; /* Release all active engines first */ pEngine = sMPGlobal.pEngines; for(;;) { if(sMPGlobal.nEngine < 1) { break; } pNext = pEngine->pNext; EngineRelease(pEngine); pEngine = pNext; sMPGlobal.nEngine--; } #if defined(PH7_ENABLE_THREADS) /* Release the mutex subsystem */ if(sMPGlobal.pMutexMethods) { if(sMPGlobal.pMutex) { SyMutexRelease(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); sMPGlobal.pMutex = 0; } if(sMPGlobal.pMutexMethods->xGlobalRelease) { sMPGlobal.pMutexMethods->xGlobalRelease(); } sMPGlobal.pMutexMethods = 0; } sMPGlobal.nThreadingLevel = 0; #endif if(sMPGlobal.sAllocator.pMethods) { /* Release the memory backend */ SyMemBackendRelease(&sMPGlobal.sAllocator); } sMPGlobal.nMagic = 0x1928; } /* * [CAPIREF: ph7_lib_shutdown()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_lib_shutdown(void) { if(sMPGlobal.nMagic != PH7_LIB_MAGIC) { /* Already shut */ return PH7_OK; } PH7CoreShutdown(); return PH7_OK; } /* * [CAPIREF: ph7_lib_is_threadsafe()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_lib_is_threadsafe(void) { if(sMPGlobal.nMagic != PH7_LIB_MAGIC) { return 0; } #if defined(PH7_ENABLE_THREADS) if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE) { /* Muli-threading support is enabled */ return 1; } else { /* Single-threading */ return 0; } #else return 0; #endif } /* * [CAPIREF: ph7_lib_version()] * Please refer to the official documentation for function purpose and expected parameters. */ const char *ph7_lib_version(void) { return PH7_VERSION; } /* * [CAPIREF: ph7_lib_signature()] * Please refer to the official documentation for function purpose and expected parameters. */ const char *ph7_lib_signature(void) { return PH7_SIG; } /* * [CAPIREF: ph7_lib_copyright()] * Please refer to the official documentation for function purpose and expected parameters. */ const char *ph7_lib_copyright(void) { return PH7_COPYRIGHT; } /* * [CAPIREF: ph7_config()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_config(ph7 *pEngine, int nConfigOp, ...) { va_list ap; int rc; if(PH7_ENGINE_MISUSE(pEngine)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif va_start(ap, nConfigOp); rc = EngineConfig(&(*pEngine), nConfigOp, ap); va_end(ap); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_init()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_init(ph7 **ppEngine) { ph7 *pEngine; int rc; #if defined(UNTRUST) if(ppEngine == 0) { return PH7_CORRUPT; } #endif *ppEngine = 0; /* One-time automatic library initialization */ rc = PH7CoreInitialize(); if(rc != PH7_OK) { return rc; } /* Allocate a new engine */ pEngine = (ph7 *)SyMemBackendPoolAlloc(&sMPGlobal.sAllocator, sizeof(ph7)); if(pEngine == 0) { return PH7_NOMEM; } /* Zero the structure */ SyZero(pEngine, sizeof(ph7)); /* Initialize engine fields */ pEngine->nMagic = PH7_ENGINE_MAGIC; rc = SyMemBackendInitFromParent(&pEngine->sAllocator, &sMPGlobal.sAllocator); if(rc != PH7_OK) { goto Release; } #if defined(PH7_ENABLE_THREADS) SyMemBackendDisbaleMutexing(&pEngine->sAllocator); #endif /* Default configuration */ SyBlobInit(&pEngine->xConf.sErrConsumer, &pEngine->sAllocator); /* Install a default compile-time error consumer routine */ ph7_config(pEngine, PH7_CONFIG_ERR_OUTPUT, PH7_VmBlobConsumer, &pEngine->xConf.sErrConsumer); /* Built-in vfs */ pEngine->pVfs = sMPGlobal.pVfs; #if defined(PH7_ENABLE_THREADS) if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE) { /* Associate a recursive mutex with this instance */ pEngine->pMutex = SyMutexNew(sMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE); if(pEngine->pMutex == 0) { rc = PH7_NOMEM; goto Release; } } #endif /* Link to the list of active engines */ #if defined(PH7_ENABLE_THREADS) /* Enter the global mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif MACRO_LD_PUSH(sMPGlobal.pEngines, pEngine); sMPGlobal.nEngine++; #if defined(PH7_ENABLE_THREADS) /* Leave the global mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif /* Write a pointer to the new instance */ *ppEngine = pEngine; return PH7_OK; Release: SyMemBackendRelease(&pEngine->sAllocator); SyMemBackendPoolFree(&sMPGlobal.sAllocator, pEngine); return rc; } /* * [CAPIREF: ph7_release()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_release(ph7 *pEngine) { int rc; if(PH7_ENGINE_MISUSE(pEngine)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Release the engine */ rc = EngineRelease(&(*pEngine)); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ /* Release engine mutex */ SyMutexRelease(sMPGlobal.pMutexMethods, pEngine->pMutex) /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ /* Enter the global mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif /* Unlink from the list of active engines */ MACRO_LD_REMOVE(sMPGlobal.pEngines, pEngine); sMPGlobal.nEngine--; #if defined(PH7_ENABLE_THREADS) /* Leave the global mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif /* Release the memory chunk allocated to this engine */ 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. * If something goes wrong [i.e: compile-time error], your error log [i.e: error consumer callback] * should display the appropriate error message and this function set ppVm to null and return * an error code that is different from PH7_OK. Otherwise when the script is successfully compiled * ppVm should hold the PH7 byte-code and it's safe to call [ph7_vm_exec(), ph7_vm_reset(), etc.]. * This API does not actually evaluate the PHP code. It merely compile and prepares the PHP script * for evaluation. */ static sxi32 ProcessSourceFile( ph7 *pEngine, /* Running PH7 engine */ ph7_vm **ppVm, /* OUT: A pointer to the virtual machine */ SyString *pScript, /* Raw PHP script to compile */ const char *zFilePath /* File path if script come from a file. NULL otherwise */ ) { ph7_vm *pVm = *ppVm; int iFileDir, rc; char *pFileDir, *fFilePath[PATH_MAX + 1]; char *pFilePath[PATH_MAX + 1]; /* 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) { snprintf(pFilePath, sizeof(pFilePath) - 1, "%s", fFilePath); /* Extract directory name containing processed file */ pFileDir = PH7_ExtractDirName(fFilePath, SyStrlen(fFilePath), &iFileDir); pFileDir[iFileDir + 1] = '\0'; /* Install local import path which is directory containing entry script */ ph7_vm_config(pVm, PH7_VM_CONFIG_IMPORT_PATH, pFileDir); /* Push processed file path */ PH7_VmPushFilePath(pVm, pFilePath, -1, TRUE, 0); } /* Compile the script */ PH7_CompileAerScript(pVm, &(*pScript), PH7_AERSCRIPT_CODE); if(pVm->sCodeGen.nErr > 0 || pVm == 0) { sxu32 nErr = pVm->sCodeGen.nErr; /* Compilation error or null ppVm pointer,release this VM */ SyMemBackendRelease(&pVm->sAllocator); SyMemBackendPoolFree(&pEngine->sAllocator, pVm); if(ppVm) { *ppVm = 0; } return nErr > 0 ? PH7_COMPILE_ERR : PH7_OK; } /* Prepare the virtual machine for bytecode execution */ rc = PH7_VmMakeReady(pVm); if(rc != PH7_OK) { goto Release; } #if defined(PH7_ENABLE_THREADS) if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE) { /* Associate a recursive mutex with this instance */ pVm->pMutex = SyMutexNew(sMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE); if(pVm->pMutex == 0) { goto Release; } } #endif /* Script successfully compiled,link to the list of active virtual machines */ MACRO_LD_PUSH(pEngine->pVms, pVm); pEngine->iVm++; /* Ready to execute PH7 bytecode */ return PH7_OK; Release: SyMemBackendRelease(&pVm->sAllocator); SyMemBackendPoolFree(&pEngine->sAllocator, pVm); *ppVm = 0; return PH7_VM_ERR; } /* * [CAPIREF: ph7_compile()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_compile_code(ph7 *pEngine, const char *zSource, int nLen, ph7_vm **ppOutVm) { SyString sScript; int rc; if(PH7_ENGINE_MISUSE(pEngine) || zSource == 0) { return PH7_CORRUPT; } if(nLen < 0) { /* Compute input length automatically */ nLen = (int)SyStrlen(zSource); } SyStringInitFromBuf(&sScript, zSource, nLen); #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Compile the script */ rc = ProcessSourceFile(&(*pEngine), ppOutVm, &sScript, 0); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif /* Compilation result */ return rc; } /* * [CAPIREF: ph7_compile_file()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_compile_file(ph7 *pEngine, const char *zFilePath, ph7_vm **ppOutVm) { const ph7_vfs *pVfs; int rc; rc = PH7_OK; /* cc warning */ if(PH7_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* * Check if the underlying vfs implement the memory map * [i.e: mmap() under UNIX/MapViewOfFile() under windows] function. */ pVfs = pEngine->pVfs; if(pVfs == 0 || pVfs->xMmap == 0) { /* Memory map routine not implemented */ rc = PH7_IO_ERR; } else { void *pMapView = 0; /* cc warning */ ph7_int64 nSize = 0; /* cc warning */ SyString sScript; /* Try to get a memory view of the whole file */ rc = pVfs->xMmap(zFilePath, &pMapView, &nSize); if(rc != PH7_OK) { /* Assume an IO error */ rc = PH7_IO_ERR; } else { /* Compile the file */ SyStringInitFromBuf(&sScript, pMapView, nSize); rc = ProcessSourceFile(&(*pEngine), ppOutVm, &sScript, zFilePath); /* Release the memory view of the whole file */ if(pVfs->xUnmap) { pVfs->xUnmap(pMapView, nSize); } } } #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif /* Compilation result */ return rc; } /* * [CAPIREF: ph7_vm_dump_v2()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_dump_v2(ph7_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData) { int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } #ifdef UNTRUST if(xConsumer == 0) { return PH7_CORRUPT; } #endif /* Dump VM instructions */ rc = PH7_VmDump(&(*pVm), xConsumer, pUserData); return rc; } /* * [CAPIREF: ph7_vm_config()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_config(ph7_vm *pVm, int iConfigOp, ...) { va_list ap; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Configure the virtual machine */ va_start(ap, iConfigOp); rc = PH7_VmConfigure(&(*pVm), iConfigOp, ap); va_end(ap); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_vm_exec()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_exec(ph7_vm *pVm, int *pExitStatus) { int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Execute PH7 byte-code */ rc = PH7_VmByteCodeExec(&(*pVm)); if(pExitStatus) { /* Exit status */ *pExitStatus = pVm->iExitStatus; } #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif /* Execution result */ return rc; } /* * [CAPIREF: ph7_vm_reset()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_reset(ph7_vm *pVm) { int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif rc = PH7_VmReset(&(*pVm)); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_vm_release()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_release(ph7_vm *pVm) { ph7 *pEngine; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif pEngine = pVm->pEngine; rc = PH7_VmRelease(&(*pVm)); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ /* free VM mutex */ SyMutexRelease(sMPGlobal.pMutexMethods, pVm->pMutex); #endif if(rc == PH7_OK) { /* Unlink from the list of active VM */ #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif MACRO_LD_REMOVE(pEngine->pVms, pVm); pEngine->iVm--; /* Release the memory chunk allocated to this VM */ SyMemBackendPoolFree(&pEngine->sAllocator, pVm); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif } return rc; } /* * [CAPIREF: ph7_create_function()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_create_function(ph7_vm *pVm, const char *zName, int (*xFunc)(ph7_context *, int, ph7_value **), void *pUserData) { SyString sName; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } SyStringInitFromBuf(&sName, zName, SyStrlen(zName)); /* Remove leading and trailing white spaces */ SyStringFullTrim(&sName); /* Ticket 1433-003: NULL values are not allowed */ if(sName.nByte < 1 || xFunc == 0) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Install the foreign function */ rc = PH7_VmInstallForeignFunction(&(*pVm), &sName, xFunc, pUserData); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_delete_function()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_delete_function(ph7_vm *pVm, const char *zName) { ph7_user_func *pFunc = 0; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Perform the deletion */ rc = SyHashDeleteEntry(&pVm->hHostFunction, (const void *)zName, SyStrlen(zName), (void **)&pFunc); if(rc == PH7_OK) { /* Release internal fields */ SySetRelease(&pFunc->aAux); SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName)); SyMemBackendPoolFree(&pVm->sAllocator, pFunc); } #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_create_constant()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_create_constant(ph7_vm *pVm, const char *zName, void (*xExpand)(ph7_value *, void *), void *pUserData) { SyString sName; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } SyStringInitFromBuf(&sName, zName, SyStrlen(zName)); /* Remove leading and trailing white spaces */ SyStringFullTrim(&sName); if(sName.nByte < 1) { /* Empty constant name */ return PH7_CORRUPT; } /* TICKET 1433-003: NULL pointer harmless operation */ if(xExpand == 0) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Perform the registration */ rc = PH7_VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_delete_constant()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_delete_constant(ph7_vm *pVm, const char *zName) { ph7_constant *pCons; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm)) { return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Query the constant hashtable */ rc = SyHashDeleteEntry(&pVm->hConstant, (const void *)zName, SyStrlen(zName), (void **)&pCons); if(rc == PH7_OK) { /* Perform the deletion */ SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pCons->sName)); SyMemBackendPoolFree(&pVm->sAllocator, pCons); } #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_new_scalar()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value *ph7_new_scalar(ph7_vm *pVm) { ph7_value *pObj; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return 0; } /* Allocate a new scalar variable */ pObj = (ph7_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_value)); if(pObj == 0) { return 0; } /* Nullify the new scalar */ PH7_MemObjInit(pVm, pObj); return pObj; } /* * [CAPIREF: ph7_new_array()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value *ph7_new_array(ph7_vm *pVm) { ph7_hashmap *pMap; ph7_value *pObj; /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return 0; } /* Create a new hashmap first */ pMap = PH7_NewHashmap(&(*pVm), 0, 0); if(pMap == 0) { return 0; } /* Associate a new ph7_value with this hashmap */ pObj = (ph7_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_value)); if(pObj == 0) { PH7_HashmapRelease(pMap, TRUE); return 0; } PH7_MemObjInitFromArray(pVm, pObj, pMap); return pObj; } /* * [CAPIREF: ph7_release_value()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_release_value(ph7_vm *pVm, ph7_value *pValue) { /* Ticket 1433-002: NULL VM is harmless operation */ if(PH7_VM_MISUSE(pVm)) { return PH7_CORRUPT; } if(pValue) { /* Release the value */ PH7_MemObjRelease(pValue); SyMemBackendPoolFree(&pVm->sAllocator, pValue); } return PH7_OK; } /* * [CAPIREF: ph7_value_to_int()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_to_int(ph7_value *pValue) { int rc; rc = PH7_MemObjToInteger(pValue); if(rc != PH7_OK) { return 0; } return (int)pValue->x.iVal; } /* * [CAPIREF: ph7_value_to_bool()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_to_bool(ph7_value *pValue) { int rc; rc = PH7_MemObjToBool(pValue); if(rc != PH7_OK) { return 0; } return (int)pValue->x.iVal; } /* * [CAPIREF: ph7_value_to_int64()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_int64 ph7_value_to_int64(ph7_value *pValue) { int rc; rc = PH7_MemObjToInteger(pValue); if(rc != PH7_OK) { return 0; } return pValue->x.iVal; } /* * [CAPIREF: ph7_value_to_double()] * Please refer to the official documentation for function purpose and expected parameters. */ double ph7_value_to_double(ph7_value *pValue) { int rc; rc = PH7_MemObjToReal(pValue); if(rc != PH7_OK) { return (double)0; } return (double)pValue->rVal; } /* * [CAPIREF: ph7_value_to_string()] * Please refer to the official documentation for function purpose and expected parameters. */ const char *ph7_value_to_string(ph7_value *pValue, int *pLen) { PH7_MemObjToString(pValue); if(SyBlobLength(&pValue->sBlob) > 0) { SyBlobNullAppend(&pValue->sBlob); if(pLen) { *pLen = (int)SyBlobLength(&pValue->sBlob); } return (const char *)SyBlobData(&pValue->sBlob); } else { /* Return the empty string */ if(pLen) { *pLen = 0; } return ""; } } /* * [CAPIREF: ph7_value_to_resource()] * Please refer to the official documentation for function purpose and expected parameters. */ void *ph7_value_to_resource(ph7_value *pValue) { if((pValue->iFlags & MEMOBJ_RES) == 0) { /* Not a resource,return NULL */ return 0; } return pValue->x.pOther; } /* * [CAPIREF: ph7_value_compare()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_compare(ph7_value *pLeft, ph7_value *pRight, int bStrict) { int rc; if(pLeft == 0 || pRight == 0) { /* TICKET 1433-24: NULL values is harmless operation */ return 1; } /* Perform the comparison */ rc = PH7_MemObjCmp(&(*pLeft), &(*pRight), bStrict, 0); /* Comparison result */ return rc; } /* * [CAPIREF: ph7_result_int()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_int(ph7_context *pCtx, int iValue) { return ph7_value_int(pCtx->pRet, iValue); } /* * [CAPIREF: ph7_result_int64()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_int64(ph7_context *pCtx, ph7_int64 iValue) { return ph7_value_int64(pCtx->pRet, iValue); } /* * [CAPIREF: ph7_result_bool()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_bool(ph7_context *pCtx, int iBool) { return ph7_value_bool(pCtx->pRet, iBool); } /* * [CAPIREF: ph7_result_double()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_double(ph7_context *pCtx, double Value) { return ph7_value_double(pCtx->pRet, Value); } /* * [CAPIREF: ph7_result_null()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_null(ph7_context *pCtx) { /* Invalidate any prior representation and set the NULL flag */ PH7_MemObjRelease(pCtx->pRet); return PH7_OK; } /* * [CAPIREF: ph7_result_string()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_string(ph7_context *pCtx, const char *zString, int nLen) { return ph7_value_string(pCtx->pRet, zString, nLen); } /* * [CAPIREF: ph7_result_string_format()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_string_format(ph7_context *pCtx, const char *zFormat, ...) { ph7_value *p; va_list ap; int rc; p = pCtx->pRet; if((p->iFlags & MEMOBJ_STRING) == 0) { /* Invalidate any prior representation */ PH7_MemObjRelease(p); MemObjSetType(p, MEMOBJ_STRING); } /* Format the given string */ va_start(ap, zFormat); rc = SyBlobFormatAp(&p->sBlob, zFormat, ap); va_end(ap); return rc; } /* * [CAPIREF: ph7_result_value()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_value(ph7_context *pCtx, ph7_value *pValue) { int rc = PH7_OK; if(pValue == 0) { PH7_MemObjRelease(pCtx->pRet); } else { rc = PH7_MemObjStore(pValue, pCtx->pRet); } return rc; } /* * [CAPIREF: ph7_result_resource()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_resource(ph7_context *pCtx, void *pUserData) { return ph7_value_resource(pCtx->pRet, pUserData); } /* * [CAPIREF: ph7_context_new_scalar()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value *ph7_context_new_scalar(ph7_context *pCtx) { ph7_value *pVal; pVal = ph7_new_scalar(pCtx->pVm); if(pVal) { /* Record value address so it can be freed automatically * when the calling function returns. */ SySetPut(&pCtx->sVar, (const void *)&pVal); } return pVal; } /* * [CAPIREF: ph7_context_new_array()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value *ph7_context_new_array(ph7_context *pCtx) { ph7_value *pVal; pVal = ph7_new_array(pCtx->pVm); if(pVal) { /* Record value address so it can be freed automatically * when the calling function returns. */ SySetPut(&pCtx->sVar, (const void *)&pVal); } return pVal; } /* * [CAPIREF: ph7_context_release_value()] * Please refer to the official documentation for function purpose and expected parameters. */ void ph7_context_release_value(ph7_context *pCtx, ph7_value *pValue) { PH7_VmReleaseContextValue(&(*pCtx), pValue); } /* * [CAPIREF: ph7_context_alloc_chunk()] * Please refer to the official documentation for function purpose and expected parameters. */ void *ph7_context_alloc_chunk(ph7_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease) { void *pChunk; pChunk = SyMemBackendAlloc(&pCtx->pVm->sAllocator, nByte); if(pChunk) { if(ZeroChunk) { /* Zero the memory chunk */ SyZero(pChunk, nByte); } if(AutoRelease) { ph7_aux_data sAux; /* Track the chunk so that it can be released automatically * upon this context is destroyed. */ sAux.pAuxData = pChunk; SySetPut(&pCtx->sChunk, (const void *)&sAux); } } return pChunk; } /* * Check if the given chunk address is registered in the call context * chunk container. * Return TRUE if registered.FALSE otherwise. * Refer to [ph7_context_realloc_chunk(),ph7_context_free_chunk()]. */ static ph7_aux_data *ContextFindChunk(ph7_context *pCtx, void *pChunk) { ph7_aux_data *aAux, *pAux; sxu32 n; if(SySetUsed(&pCtx->sChunk) < 1) { /* Don't bother processing,the container is empty */ return 0; } /* Perform the lookup */ aAux = (ph7_aux_data *)SySetBasePtr(&pCtx->sChunk); for(n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n) { pAux = &aAux[n]; if(pAux->pAuxData == pChunk) { /* Chunk found */ return pAux; } } /* No such allocated chunk */ return 0; } /* * [CAPIREF: ph7_context_realloc_chunk()] * Please refer to the official documentation for function purpose and expected parameters. */ void *ph7_context_realloc_chunk(ph7_context *pCtx, void *pChunk, unsigned int nByte) { ph7_aux_data *pAux; void *pNew; pNew = SyMemBackendRealloc(&pCtx->pVm->sAllocator, pChunk, nByte); if(pNew) { pAux = ContextFindChunk(pCtx, pChunk); if(pAux) { pAux->pAuxData = pNew; } } return pNew; } /* * [CAPIREF: ph7_context_free_chunk()] * Please refer to the official documentation for function purpose and expected parameters. */ void ph7_context_free_chunk(ph7_context *pCtx, void *pChunk) { ph7_aux_data *pAux; if(pChunk == 0) { /* TICKET-1433-93: NULL chunk is a harmless operation */ return; } pAux = ContextFindChunk(pCtx, pChunk); if(pAux) { /* Mark as destroyed */ pAux->pAuxData = 0; } SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk); } /* * [CAPIREF: ph7_array_fetch()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value *ph7_array_fetch(ph7_value *pArray, const char *zKey, int nByte) { ph7_hashmap_node *pNode; ph7_value *pValue; ph7_value skey; int rc; /* Make sure we are dealing with a valid hashmap */ if((pArray->iFlags & MEMOBJ_HASHMAP) == 0) { return 0; } if(nByte < 0) { nByte = (int)SyStrlen(zKey); } /* Convert the key to a ph7_value */ PH7_MemObjInit(pArray->pVm, &skey); PH7_MemObjStringAppend(&skey, zKey, (sxu32)nByte); /* Perform the lookup */ rc = PH7_HashmapLookup((ph7_hashmap *)pArray->x.pOther, &skey, &pNode); PH7_MemObjRelease(&skey); if(rc != PH7_OK) { /* No such entry */ return 0; } /* Extract the target value */ pValue = (ph7_value *)SySetAt(&pArray->pVm->aMemObj, pNode->nValIdx); return pValue; } /* * [CAPIREF: ph7_array_walk()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_array_walk(ph7_value *pArray, int (*xWalk)(ph7_value *pValue, ph7_value *, void *), void *pUserData) { int rc; if(xWalk == 0) { return PH7_CORRUPT; } /* Make sure we are dealing with a valid hashmap */ if((pArray->iFlags & MEMOBJ_HASHMAP) == 0) { return PH7_CORRUPT; } /* Start the walk process */ rc = PH7_HashmapWalk((ph7_hashmap *)pArray->x.pOther, xWalk, pUserData); return rc != PH7_OK ? PH7_ABORT /* User callback request an operation abort*/ : PH7_OK; } /* * [CAPIREF: ph7_array_add_elem()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_array_add_elem(ph7_value *pArray, ph7_value *pKey, ph7_value *pValue) { int rc; /* Make sure we are dealing with a valid hashmap */ if((pArray->iFlags & MEMOBJ_HASHMAP) == 0) { return PH7_CORRUPT; } /* Perform the insertion */ rc = PH7_HashmapInsert((ph7_hashmap *)pArray->x.pOther, &(*pKey), &(*pValue)); return rc; } /* * [CAPIREF: ph7_array_add_strkey_elem()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_array_add_strkey_elem(ph7_value *pArray, const char *zKey, ph7_value *pValue) { int rc; /* Make sure we are dealing with a valid hashmap */ if((pArray->iFlags & MEMOBJ_HASHMAP) == 0) { return PH7_CORRUPT; } /* Perform the insertion */ if(SX_EMPTY_STR(zKey)) { /* Empty key,assign an automatic index */ rc = PH7_HashmapInsert((ph7_hashmap *)pArray->x.pOther, 0, &(*pValue)); } else { ph7_value sKey; PH7_MemObjInitFromString(pArray->pVm, &sKey, 0); PH7_MemObjStringAppend(&sKey, zKey, (sxu32)SyStrlen(zKey)); rc = PH7_HashmapInsert((ph7_hashmap *)pArray->x.pOther, &sKey, &(*pValue)); PH7_MemObjRelease(&sKey); } return rc; } /* * [CAPIREF: ph7_array_add_intkey_elem()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_array_add_intkey_elem(ph7_value *pArray, int iKey, ph7_value *pValue) { ph7_value sKey; int rc; /* Make sure we are dealing with a valid hashmap */ if((pArray->iFlags & MEMOBJ_HASHMAP) == 0) { return PH7_CORRUPT; } PH7_MemObjInitFromInt(pArray->pVm, &sKey, iKey); /* Perform the insertion */ rc = PH7_HashmapInsert((ph7_hashmap *)pArray->x.pOther, &sKey, &(*pValue)); PH7_MemObjRelease(&sKey); return rc; } /* * [CAPIREF: ph7_array_count()] * Please refer to the official documentation for function purpose and expected parameters. */ unsigned int ph7_array_count(ph7_value *pArray) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if((pArray->iFlags & MEMOBJ_HASHMAP) == 0) { return 0; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)pArray->x.pOther; return pMap->nEntry; } /* * [CAPIREF: ph7_object_walk()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_object_walk(ph7_value *pObject, int (*xWalk)(const char *, ph7_value *, void *), void *pUserData) { int rc; if(xWalk == 0) { return PH7_CORRUPT; } /* Make sure we are dealing with a valid class instance */ if((pObject->iFlags & MEMOBJ_OBJ) == 0) { return PH7_CORRUPT; } /* Start the walk process */ rc = PH7_ClassInstanceWalk((ph7_class_instance *)pObject->x.pOther, xWalk, pUserData); return rc != PH7_OK ? PH7_ABORT /* User callback request an operation abort*/ : PH7_OK; } /* * [CAPIREF: ph7_object_fetch_attr()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value *ph7_object_fetch_attr(ph7_value *pObject, const char *zAttr) { ph7_value *pValue; SyString sAttr; /* Make sure we are dealing with a valid class instance */ if((pObject->iFlags & MEMOBJ_OBJ) == 0 || zAttr == 0) { return 0; } SyStringInitFromBuf(&sAttr, zAttr, SyStrlen(zAttr)); /* Extract the attribute value if available. */ pValue = PH7_ClassInstanceFetchAttr((ph7_class_instance *)pObject->x.pOther, &sAttr); return pValue; } /* * [CAPIREF: ph7_object_get_class_name()] * Please refer to the official documentation for function purpose and expected parameters. */ const char *ph7_object_get_class_name(ph7_value *pObject, int *pLength) { ph7_class *pClass; if(pLength) { *pLength = 0; } /* Make sure we are dealing with a valid class instance */ if((pObject->iFlags & MEMOBJ_OBJ) == 0) { return 0; } /* Point to the class */ pClass = ((ph7_class_instance *)pObject->x.pOther)->pClass; /* Return the class name */ if(pLength) { *pLength = (int)SyStringLength(&pClass->sName); } return SyStringData(&pClass->sName); } /* * [CAPIREF: ph7_context_output()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_output(ph7_context *pCtx, const char *zString, int nLen) { SyString sData; int rc; if(nLen < 0) { nLen = (int)SyStrlen(zString); } SyStringInitFromBuf(&sData, zString, nLen); rc = PH7_VmOutputConsume(pCtx->pVm, &sData); return rc; } /* * [CAPIREF: ph7_context_output_format()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_output_format(ph7_context *pCtx, const char *zFormat, ...) { va_list ap; int rc; va_start(ap, zFormat); rc = PH7_VmOutputConsumeAp(pCtx->pVm, zFormat, ap); va_end(ap); return rc; } /* * [CAPIREF: ph7_context_throw_error()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_throw_error(ph7_context *pCtx, int iErr, const char *zErr) { int rc = PH7_OK; if(zErr) { rc = PH7_VmThrowError(pCtx->pVm, &pCtx->pFunc->sName, iErr, zErr); } return rc; } /* * [CAPIREF: ph7_context_throw_error_format()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_throw_error_format(ph7_context *pCtx, int iErr, const char *zFormat, ...) { va_list ap; int rc; if(zFormat == 0) { return PH7_OK; } va_start(ap, zFormat); rc = PH7_VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap); va_end(ap); return rc; } /* * [CAPIREF: ph7_context_random_num()] * Please refer to the official documentation for function purpose and expected parameters. */ unsigned int ph7_context_random_num(ph7_context *pCtx) { sxu32 n; n = PH7_VmRandomNum(pCtx->pVm); return n; } /* * [CAPIREF: ph7_context_random_string()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_random_string(ph7_context *pCtx, char *zBuf, int nBuflen) { if(nBuflen < 3) { return PH7_CORRUPT; } PH7_VmRandomString(pCtx->pVm, zBuf, nBuflen); return PH7_OK; } /* * IMP-12-07-2012 02:10 Experimantal public API. * * ph7_vm * ph7_context_get_vm(ph7_context *pCtx) * { * return pCtx->pVm; * } */ /* * [CAPIREF: ph7_context_user_data()] * Please refer to the official documentation for function purpose and expected parameters. */ void *ph7_context_user_data(ph7_context *pCtx) { return pCtx->pFunc->pUserData; } /* * [CAPIREF: ph7_context_push_aux_data()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_push_aux_data(ph7_context *pCtx, void *pUserData) { ph7_aux_data sAux; int rc; sAux.pAuxData = pUserData; rc = SySetPut(&pCtx->pFunc->aAux, (const void *)&sAux); return rc; } /* * [CAPIREF: ph7_context_peek_aux_data()] * Please refer to the official documentation for function purpose and expected parameters. */ void *ph7_context_peek_aux_data(ph7_context *pCtx) { ph7_aux_data *pAux; pAux = (ph7_aux_data *)SySetPeek(&pCtx->pFunc->aAux); return pAux ? pAux->pAuxData : 0; } /* * [CAPIREF: ph7_context_pop_aux_data()] * Please refer to the official documentation for function purpose and expected parameters. */ void *ph7_context_pop_aux_data(ph7_context *pCtx) { ph7_aux_data *pAux; pAux = (ph7_aux_data *)SySetPop(&pCtx->pFunc->aAux); return pAux ? pAux->pAuxData : 0; } /* * [CAPIREF: ph7_context_result_buf_length()] * Please refer to the official documentation for function purpose and expected parameters. */ unsigned int ph7_context_result_buf_length(ph7_context *pCtx) { return SyBlobLength(&pCtx->pRet->sBlob); } /* * [CAPIREF: ph7_function_name()] * Please refer to the official documentation for function purpose and expected parameters. */ const char *ph7_function_name(ph7_context *pCtx) { SyString *pName; pName = &pCtx->pFunc->sName; return pName->zString; } /* * [CAPIREF: ph7_value_int()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_int(ph7_value *pVal, int iValue) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); pVal->x.iVal = (ph7_int64)iValue; MemObjSetType(pVal, MEMOBJ_INT); return PH7_OK; } /* * [CAPIREF: ph7_value_int64()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_int64(ph7_value *pVal, ph7_int64 iValue) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); pVal->x.iVal = iValue; MemObjSetType(pVal, MEMOBJ_INT); return PH7_OK; } /* * [CAPIREF: ph7_value_bool()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_bool(ph7_value *pVal, int iBool) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); pVal->x.iVal = iBool ? 1 : 0; MemObjSetType(pVal, MEMOBJ_BOOL); return PH7_OK; } /* * [CAPIREF: ph7_value_null()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_null(ph7_value *pVal) { /* Invalidate any prior representation and set the NULL flag */ PH7_MemObjRelease(pVal); return PH7_OK; } /* * [CAPIREF: ph7_value_double()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_double(ph7_value *pVal, double Value) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); pVal->rVal = (ph7_real)Value; MemObjSetType(pVal, MEMOBJ_REAL); /* Try to get an integer representation also */ PH7_MemObjTryInteger(pVal); return PH7_OK; } /* * [CAPIREF: ph7_value_string()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_string(ph7_value *pVal, const char *zString, int nLen) { if((pVal->iFlags & MEMOBJ_STRING) == 0) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); MemObjSetType(pVal, MEMOBJ_STRING); } if(zString) { if(nLen < 0) { /* Compute length automatically */ nLen = (int)SyStrlen(zString); } SyBlobAppend(&pVal->sBlob, (const void *)zString, (sxu32)nLen); } return PH7_OK; } /* * [CAPIREF: ph7_value_string_format()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_string_format(ph7_value *pVal, const char *zFormat, ...) { va_list ap; int rc; if((pVal->iFlags & MEMOBJ_STRING) == 0) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); MemObjSetType(pVal, MEMOBJ_STRING); } va_start(ap, zFormat); rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap); va_end(ap); return PH7_OK; } /* * [CAPIREF: ph7_value_reset_string_cursor()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_reset_string_cursor(ph7_value *pVal) { /* Reset the string cursor */ SyBlobReset(&pVal->sBlob); return PH7_OK; } /* * [CAPIREF: ph7_value_resource()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_resource(ph7_value *pVal, void *pUserData) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); /* Reflect the new type */ pVal->x.pOther = pUserData; MemObjSetType(pVal, MEMOBJ_RES); return PH7_OK; } /* * [CAPIREF: ph7_value_release()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_release(ph7_value *pVal) { PH7_MemObjRelease(pVal); return PH7_OK; } /* * [CAPIREF: ph7_value_is_int()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_int(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_INT) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_float()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_float(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_REAL) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_bool()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_bool(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_string()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_string(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_null()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_null(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_numeric()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_numeric(ph7_value *pVal) { int rc; rc = PH7_MemObjIsNumeric(pVal); return rc; } /* * [CAPIREF: ph7_value_is_callable()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_callable(ph7_value *pVal) { int rc; rc = PH7_VmIsCallable(pVal->pVm, pVal, FALSE); return rc; } /* * [CAPIREF: ph7_value_is_scalar()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_scalar(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_SCALAR) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_array()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_array(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_HASHMAP) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_object()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_object(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_OBJ) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_resource()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_resource(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_empty()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_empty(ph7_value *pVal) { int rc; rc = PH7_MemObjIsEmpty(pVal); return rc; }