|
|
|
/*
|
|
|
|
* 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 <chm@symisc.net> $ */
|
|
|
|
#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_MEM_LIMIT: {
|
|
|
|
char *sMemLimit = va_arg(ap, char *);
|
|
|
|
if(!sMemLimit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
char *sLimitRem;
|
|
|
|
sxu64 nMemLimit;
|
|
|
|
SyStrToInt64(sMemLimit, SyStrlen(sMemLimit), (void *)&nMemLimit, &sLimitRem);
|
|
|
|
if(sLimitRem) {
|
|
|
|
switch(*sLimitRem) {
|
|
|
|
case 'G':
|
|
|
|
case 'g':
|
|
|
|
nMemLimit *= 1024;
|
|
|
|
case 'M':
|
|
|
|
case 'm':
|
|
|
|
nMemLimit *= 1024;
|
|
|
|
case 'K':
|
|
|
|
case 'k':
|
|
|
|
nMemLimit *= 1024;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(nMemLimit >= 1048576) {
|
|
|
|
/* At least 1MB of heap */
|
|
|
|
pEngine->sAllocator.pHeap->nLimit = nMemLimit;
|
|
|
|
} else {
|
|
|
|
/* Fallback to no limit */
|
|
|
|
pEngine->sAllocator.pHeap->nLimit = 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);
|
|
|
|
}
|
|