Aer Interpreter Source
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

11122 lines
348 KiB

/**
* @PROJECT PH7 Engine for the AerScript Interpreter
* @COPYRIGHT See COPYING in the top level directory
* @FILE engine/vm.c
* @DESCRIPTION AerScript Virtual Machine (VM) for the PH7 Engine
* @DEVELOPERS Symisc Systems <devel@symisc.net>
* Rafal Kupiec <belliash@codingworkshop.eu.org>
* David Carlier <devnexen@gmail.com>
*/
#include "ph7int.h"
/*
* Each parsed URI is recorded and stored in an instance of the following structure.
* This structure and it's related routines are taken verbatim from the xHT project
* [A modern embeddable HTTP engine implementing all the RFC2616 methods]
* the xHT project is developed internally by Symisc Systems.
*/
typedef struct SyhttpUri SyhttpUri;
3 years ago
struct SyhttpUri {
SyString sHost; /* Hostname or IP address */
SyString sPort; /* Port number */
SyString sPath; /* Mandatory resource path passed verbatim (Not decoded) */
SyString sQuery; /* Query part */
SyString sFragment; /* Fragment part */
SyString sScheme; /* Scheme */
SyString sUser; /* Username */
SyString sPass; /* Password */
SyString sRaw; /* Raw URI */
};
3 years ago
/*
* An instance of the following structure is used to record all MIME headers seen
3 years ago
* during a HTTP interaction.
* This structure and it's related routines are taken verbatim from the xHT project
* [A modern embeddable HTTP engine implementing all the RFC2616 methods]
* the xHT project is developed internally by Symisc Systems.
3 years ago
*/
typedef struct SyhttpHeader SyhttpHeader;
3 years ago
struct SyhttpHeader {
SyString sName; /* Header name [i.e:"Content-Type","Host","User-Agent"]. NOT NUL TERMINATED */
SyString sValue; /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */
};
/*
* Supported HTTP methods.
*/
#define HTTP_METHOD_GET 1 /* GET */
#define HTTP_METHOD_HEAD 2 /* HEAD */
#define HTTP_METHOD_POST 3 /* POST */
#define HTTP_METHOD_PUT 4 /* PUT */
#define HTTP_METHOD_OTHER 5 /* Other HTTP methods [i.e: DELETE,TRACE,OPTIONS...]*/
/*
* Supported HTTP protocol version.
*/
#define HTTP_PROTO_10 1 /* HTTP/1.0 */
#define HTTP_PROTO_11 2 /* HTTP/1.1 */
/*
* Register a constant and it's associated expansion callback so that
* it can be expanded from the target PHP program.
* The constant expansion mechanism under PH7 is extremely powerful yet
* simple and work as follows:
* Each registered constant have a C procedure associated with it.
* This procedure known as the constant expansion callback is responsible
* of expanding the invoked constant to the desired value,for example:
* The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI).
* The "__OS__" constant procedure expands to the name of the host Operating Systems
* (Windows,Linux,...) and so on.
* Please refer to the official documentation for additional information.
*/
PH7_PRIVATE sxi32 PH7_VmRegisterConstant(
ph7_vm *pVm, /* Target VM */
const SyString *pName, /* Constant name */
ProcConstant xExpand, /* Constant expansion callback */
void *pUserData, /* Last argument to xExpand() */
sxbool bGlobal /* Whether this is a global constant or not */
3 years ago
) {
ph7_constant *pCons;
SyHash *pCollection;
SyHashEntry *pEntry;
char *zDupName;
sxi32 rc;
if(bGlobal) {
pCollection = &pVm->hConstant;
} else {
pCollection = &pVm->pFrame->hConst;
}
pEntry = SyHashGet(pCollection, (const void *)pName->zString, pName->nByte);
3 years ago
if(pEntry) {
/* Constant already exists */
return SXERR_EXISTS;
}
/* Allocate a new constant instance */
3 years ago
pCons = (ph7_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_constant));
if(pCons == 0) {
PH7_VmMemoryError(&(*pVm));
}
/* Duplicate constant name */
3 years ago
zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
if(zDupName == 0) {
PH7_VmMemoryError(&(*pVm));
}
/* Install the constant */
3 years ago
SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte);
pCons->xExpand = xExpand;
pCons->pUserData = pUserData;
rc = SyHashInsert(pCollection, (const void *)zDupName, SyStringLength(&pCons->sName), pCons);
3 years ago
if(rc != SXRET_OK) {
PH7_VmMemoryError(&(*pVm));
}
/* All done,constant can be invoked from PHP code */
return SXRET_OK;
}
/*
* Allocate a new foreign function instance.
* This function return SXRET_OK on success. Any other
* return value indicates failure.
* Please refer to the official documentation for an introduction to
* the foreign function mechanism.
*/
static sxi32 PH7_NewForeignFunction(
ph7_vm *pVm, /* Target VM */
const SyString *pName, /* Foreign function name */
3 years ago
ProcHostFunction xFunc, /* Foreign function implementation */
void *pUserData, /* Foreign function private data */
ph7_user_func **ppOut /* OUT: VM image of the foreign function */
3 years ago
) {
ph7_user_func *pFunc;
char *zDup;
/* Allocate a new user function */
3 years ago
pFunc = (ph7_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_user_func));
if(pFunc == 0) {
return SXERR_MEM;
}
/* Duplicate function name */
3 years ago
zDup = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
if(zDup == 0) {
SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
return SXERR_MEM;
}
/* Zero the structure */
3 years ago
SyZero(pFunc, sizeof(ph7_user_func));
/* Initialize structure fields */
3 years ago
SyStringInitFromBuf(&pFunc->sName, zDup, pName->nByte);
pFunc->pVm = pVm;
pFunc->xFunc = xFunc;
pFunc->pUserData = pUserData;
3 years ago
SySetInit(&pFunc->aAux, &pVm->sAllocator, sizeof(ph7_aux_data));
/* Write a pointer to the new function */
*ppOut = pFunc;
return SXRET_OK;
}
/*
* Install a foreign function and it's associated callback so that
* it can be invoked from the target PHP code.
* This function return SXRET_OK on successful registration. Any other
* return value indicates failure.
* Please refer to the official documentation for an introduction to
* the foreign function mechanism.
*/
PH7_PRIVATE sxi32 PH7_VmInstallForeignFunction(
ph7_vm *pVm, /* Target VM */
const SyString *pName, /* Foreign function name */
3 years ago
ProcHostFunction xFunc, /* Foreign function implementation */
void *pUserData /* Foreign function private data */
3 years ago
) {
ph7_user_func *pFunc;
SyHashEntry *pEntry;
sxi32 rc;
/* Overwrite any previously registered function with the same name */
3 years ago
pEntry = SyHashGet(&pVm->hHostFunction, pName->zString, pName->nByte);
if(pEntry) {
pFunc = (ph7_user_func *)pEntry->pUserData;
pFunc->pUserData = pUserData;
pFunc->xFunc = xFunc;
SySetReset(&pFunc->aAux);
return SXRET_OK;
}
/* Create a new user function */
3 years ago
rc = PH7_NewForeignFunction(&(*pVm), &(*pName), xFunc, pUserData, &pFunc);
if(rc != SXRET_OK) {
return rc;
}
/* Install the function in the corresponding hashtable */
3 years ago
rc = SyHashInsert(&pVm->hHostFunction, SyStringData(&pFunc->sName), pName->nByte, pFunc);
if(rc != SXRET_OK) {
SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
return rc;
}
/* User function successfully installed */
return SXRET_OK;
}
/*
* Initialize a VM function.
*/
PH7_PRIVATE sxi32 PH7_VmInitFuncState(
ph7_vm *pVm, /* Target VM */
ph7_vm_func *pFunc, /* Target Function */
const char *zName, /* Function name */
sxu32 nByte, /* zName length */
sxi32 iFlags, /* Configuration flags */
void *pUserData /* Function private data */
3 years ago
) {
/* Zero the structure */
3 years ago
SyZero(pFunc, sizeof(ph7_vm_func));
/* Initialize structure fields */
/* Arguments container */
3 years ago
SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(ph7_vm_func_arg));
/* Static variable container */
3 years ago
SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(ph7_vm_func_static_var));
/* Bytecode container */
3 years ago
SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
/* Preallocate some instruction slots */
SySetAlloc(&pFunc->aByteCode, 0x10);
/* Closure environment */
3 years ago
SySetInit(&pFunc->aClosureEnv, &pVm->sAllocator, sizeof(ph7_vm_func_closure_env));
pFunc->iFlags = iFlags;
pFunc->pUserData = pUserData;
3 years ago
SyStringInitFromBuf(&pFunc->sName, zName, nByte);
return SXRET_OK;
}
/*
* Install a user defined function in the corresponding VM container.
*/
PH7_PRIVATE sxi32 PH7_VmInstallUserFunction(
ph7_vm *pVm, /* Target VM */
ph7_vm_func *pFunc, /* Target function */
SyString *pName /* Function name */
3 years ago
) {
SyHashEntry *pEntry;
sxi32 rc;
3 years ago
if(pName == 0) {
/* Use the built-in name */
pName = &pFunc->sName;
}
/* Check for duplicates (functions with the same name) first */
3 years ago
pEntry = SyHashGet(&pVm->hFunction, pName->zString, pName->nByte);
if(pEntry) {
ph7_vm_func *pLink = (ph7_vm_func *)pEntry->pUserData;
3 years ago
if(pLink != pFunc) {
/* Link */
pFunc->pNextName = pLink;
pEntry->pUserData = pFunc;
}
return SXRET_OK;
}
/* First time seen */
pFunc->pNextName = 0;
3 years ago
rc = SyHashInsert(&pVm->hFunction, pName->zString, pName->nByte, pFunc);
return rc;
}
/*
* Install a user defined class in the corresponding VM container.
*/
PH7_PRIVATE sxi32 PH7_VmInstallClass(
ph7_vm *pVm, /* Target VM */
ph7_class *pClass /* Target Class */
3 years ago
) {
SyString *pName = &pClass->sName;
SyHashEntry *pEntry;
sxi32 rc;
/* Check for duplicates */
3 years ago
pEntry = SyHashGet(&pVm->hClass, (const void *)pName->zString, pName->nByte);
if(pEntry) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot declare class, because the name is already in use");
}
/* Perform a simple hashtable insertion */
3 years ago
rc = SyHashInsert(&pVm->hClass, (const void *)pName->zString, pName->nByte, pClass);
return rc;
}
/*
* Instruction builder interface.
*/
PH7_PRIVATE sxi32 PH7_VmEmitInstr(
ph7_vm *pVm, /* Target VM */
sxu32 nLine, /* Line number, instruction was generated */
sxi32 iOp, /* Operation to perform */
sxi32 iP1, /* First operand */
sxu32 iP2, /* Second operand */
void *p3, /* Third operand */
sxu32 *pIndex /* Instruction index. NULL otherwise */
3 years ago
) {
VmInstr sInstr;
sxi32 rc;
/* Extract the processed script */
SyString *pFile = (SyString *)SySetPeek(&pVm->aFiles);
static const SyString sFileName = { "[MEMORY]", sizeof("[MEMORY]") - 1};
if(pFile == 0) {
pFile = (SyString *)&sFileName;
}
/* Fill the VM instruction */
3 years ago
sInstr.iOp = (sxu8)iOp;
sInstr.iP1 = iP1;
sInstr.iP2 = iP2;
sInstr.p3 = p3;
sInstr.bExec = FALSE;
sInstr.pFile = pFile;
sInstr.iLine = 1;
if(nLine > 0) {
sInstr.iLine = nLine;
} else if(pVm->sCodeGen.pEnd && pVm->sCodeGen.pEnd->nLine > 0) {
sInstr.iLine = pVm->sCodeGen.pEnd->nLine;
}
3 years ago
if(pIndex) {
/* Instruction index in the bytecode array */
*pIndex = SySetUsed(pVm->pByteContainer);
}
/* Finally,record the instruction */
3 years ago
rc = SySetPut(pVm->pByteContainer, (const void *)&sInstr);
if(rc != SXRET_OK) {
PH7_GenCompileError(&pVm->sCodeGen, E_ERROR, 1, "Fatal,Cannot emit instruction due to a memory failure");
/* Fall throw */
}
return rc;
}
/*
* Swap the current bytecode container with the given one.
*/
3 years ago
PH7_PRIVATE sxi32 PH7_VmSetByteCodeContainer(ph7_vm *pVm, SySet *pContainer) {
if(pContainer == 0) {
/* Point to the default container */
pVm->pByteContainer = &pVm->aByteCode;
3 years ago
} else {
/* Change container */
pVm->pByteContainer = &(*pContainer);
}
return SXRET_OK;
}
/*
* Return the current bytecode container.
*/
3 years ago
PH7_PRIVATE SySet *PH7_VmGetByteCodeContainer(ph7_vm *pVm) {
return pVm->pByteContainer;
}
/*
* Extract the VM instruction rooted at nIndex.
*/
3 years ago
PH7_PRIVATE VmInstr *PH7_VmGetInstr(ph7_vm *pVm, sxu32 nIndex) {
VmInstr *pInstr;
3 years ago
pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex);
return pInstr;
}
/*
* Return the total number of VM instructions recorded so far.
*/
3 years ago
PH7_PRIVATE sxu32 PH7_VmInstrLength(ph7_vm *pVm) {
return SySetUsed(pVm->pByteContainer);
}
/*
* Pop the last VM instruction.
*/
3 years ago
PH7_PRIVATE VmInstr *PH7_VmPopInstr(ph7_vm *pVm) {
return (VmInstr *)SySetPop(pVm->pByteContainer);
}
/*
* Peek the last VM instruction.
*/
3 years ago
PH7_PRIVATE VmInstr *PH7_VmPeekInstr(ph7_vm *pVm) {
return (VmInstr *)SySetPeek(pVm->pByteContainer);
}
3 years ago
PH7_PRIVATE VmInstr *PH7_VmPeekNextInstr(ph7_vm *pVm) {
VmInstr *aInstr;
sxu32 n;
n = SySetUsed(pVm->pByteContainer);
3 years ago
if(n < 2) {
return 0;
}
aInstr = (VmInstr *)SySetBasePtr(pVm->pByteContainer);
return &aInstr[n - 2];
}
/*
* Allocate a new virtual machine frame.
*/
3 years ago
static VmFrame *VmNewFrame(
ph7_vm *pVm, /* Target VM */
void *pUserData, /* Upper-layer private data */
ph7_class_instance *pThis /* Top most class instance [i.e: Object in the PHP jargon]. NULL otherwise */
3 years ago
) {
VmFrame *pFrame;
/* Allocate a new vm frame */
3 years ago
pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmFrame));
if(pFrame == 0) {
return 0;
}
/* Zero the structure */
3 years ago
SyZero(pFrame, sizeof(VmFrame));
/* Initialize frame fields */
pFrame->pUserData = pUserData;
pFrame->pThis = pThis;
pFrame->pVm = pVm;
SyHashInit(&pFrame->hConst, &pVm->sAllocator, 0, 0);
3 years ago
SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0);
SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot));
SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot));
SySetInit(&pFrame->sRef, &pVm->sAllocator, sizeof(VmSlot));
return pFrame;
}
/*
* Enter a VM frame.
*/
static sxi32 VmEnterFrame(
ph7_vm *pVm, /* Target VM */
void *pUserData, /* Upper-layer private data */
ph7_class_instance *pThis, /* Top most class instance [i.e: Object in the PHP jargon]. NULL otherwise */
VmFrame **ppFrame /* OUT: Top most active frame */
3 years ago
) {
VmFrame *pFrame;
/* Allocate a new frame */
3 years ago
pFrame = VmNewFrame(&(*pVm), pUserData, pThis);
if(pFrame == 0) {
return SXERR_MEM;
}
/* Link to the list of active VM frame */
pFrame->pParent = pVm->pFrame;
pVm->pFrame = pFrame;
3 years ago
if(ppFrame) {
/* Write a pointer to the new VM frame */
*ppFrame = pFrame;
}
return SXRET_OK;
}
/*
* Leave the top-most active frame.
*/
3 years ago
static void VmLeaveFrame(ph7_vm *pVm) {
VmFrame *pFrame = pVm->pFrame;
3 years ago
if(pFrame) {
/* Unlink from the list of active VM frame */
pVm->pFrame = pFrame->pParent;
3 years ago
if(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) == 0) {
VmSlot *aSlot;
sxu32 n;
/* Restore local variable to the free pool so that they can be reused again */
aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal);
3 years ago
for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n) {
/* Unset the local variable */
3 years ago
PH7_VmUnsetMemObj(&(*pVm), aSlot[n].nIdx, FALSE);
}
/* Remove local reference */
aSlot = (VmSlot *)SySetBasePtr(&pFrame->sRef);
3 years ago
for(n = 0 ; n < SySetUsed(&pFrame->sRef) ; ++n) {
PH7_VmRefObjRemove(&(*pVm), aSlot[n].nIdx, (SyHashEntry *)aSlot[n].pUserData, 0);
}
}
/* Release internal containers */
SyHashRelease(&pFrame->hConst);
SyHashRelease(&pFrame->hVar);
SySetRelease(&pFrame->sArg);
SySetRelease(&pFrame->sLocal);
SySetRelease(&pFrame->sRef);
/* Release the whole structure */
3 years ago
SyMemBackendPoolFree(&pVm->sAllocator, pFrame);
}
}
/*
* Compare two functions signature and return the comparison result.
*/
3 years ago
static int VmOverloadCompare(SyString *pFirst, SyString *pSecond) {
const char *zSend = &pSecond->zString[pSecond->nByte];
const char *zFend = &pFirst->zString[pFirst->nByte];
const char *zSin = pSecond->zString;
const char *zFin = pFirst->zString;
const char *zPtr = zFin;
3 years ago
for(;;) {
if(zFin >= zFend || zSin >= zSend) {
break;
}
3 years ago
if(zFin[0] != zSin[0]) {
/* mismatch */
break;
}
zFin++;
zSin++;
}
3 years ago
return (int)(zFin - zPtr);
}
/* Forward declaration */
static sxi32 VmLocalExec(ph7_vm *pVm, SySet *pByteCode, ph7_value *pResult);
/*
* Select the appropriate VM function for the current call context.
* This is the implementation of the powerful 'function overloading' feature
* introduced by the version 2 of the PH7 engine.
* Refer to the official documentation for more information.
*/
3 years ago
static ph7_vm_func *VmOverload(
ph7_vm *pVm, /* Target VM */
ph7_vm_func *pList, /* Linked list of candidates for overloading */
ph7_value *aArg, /* Array of passed arguments */
int nArg /* Total number of passed arguments */
3 years ago
) {
int iTarget, i, j, iArgs, iCur, iMax;
ph7_vm_func *apSet[10]; /* Maximum number of candidates */
ph7_vm_func *pLink;
ph7_vm_func_arg *pFuncArg;
SyString sArgSig;
SyBlob sSig;
pLink = pList;
i = 0;
/* Put functions expecting the same number of passed arguments */
3 years ago
while(i < (int)SX_ARRAYSIZE(apSet)) {
if(pLink == 0) {
break;
}
iArgs = (int) SySetUsed(&pLink->aArgs);
if(nArg == iArgs) {
/* Exact amount of parameters, a candidate to call */
apSet[i++] = pLink;
} else if(nArg < iArgs) {
/* Fewer parameters passed, check if all are required */
pFuncArg = (ph7_vm_func_arg *) SySetAt(&pLink->aArgs, nArg);
if(pFuncArg) {
if(SySetUsed(&pFuncArg->aByteCode) >= 1) {
/* First missing parameter has a compiled default value associated, a candidate to call */
apSet[i++] = pLink;
}
}
}
/* Point to the next entry */
pLink = pLink->pNextName;
}
3 years ago
if(i < 1) {
/* No candidates, throw an error */
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Invalid number of arguments passed to function/method '%z()'", &pList->sName);
}
3 years ago
if(nArg < 1 || i < 2) {
/* Return the only candidate */
return apSet[0];
}
/* Calculate function signature */
3 years ago
SyBlobInit(&sSig, &pVm->sAllocator);
for(j = 0 ; j < nArg ; j++) {
int c = 'n'; /* null */
if(aArg[j].nType & MEMOBJ_BOOL) {
/* Bool */
c = 'b';
} else if(aArg[j].nType & MEMOBJ_CALL) {
/* Callback */
c = 'a';
} else if(aArg[j].nType & MEMOBJ_CHAR) {
/* Char */
c = 'c';
} else if(aArg[j].nType & MEMOBJ_INT) {
/* Integer */
c = 'i';
} else if(aArg[j].nType & MEMOBJ_MIXED) {
/* Mixed */
c = 'm';
} else if(aArg[j].nType & MEMOBJ_OBJ) {
/* Class instance */
ph7_class *pClass = ((ph7_class_instance *)aArg[j].x.pOther)->pClass;
SyString *pName = &pClass->sName;
3 years ago
SyBlobAppend(&sSig, (const void *)pName->zString, pName->nByte);
c = -1;
} else if(aArg[j].nType & MEMOBJ_REAL) {
/* Float */
c = 'f';
} else if(aArg[j].nType & MEMOBJ_RES) {
/* Resource */
c = 'r';
} else if(aArg[j].nType & MEMOBJ_STRING) {
/* String */
c = 's';
} else if(aArg[j].nType & MEMOBJ_VOID) {
/* Void */
c = 'v';
}
if(aArg[j].nType & MEMOBJ_HASHMAP && (aArg[j].nType & MEMOBJ_OBJ) == 0) {
c = SyToUpper(c);
}
3 years ago
if(c > 0) {
SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
}
}
3 years ago
SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig));
iTarget = 0;
iMax = -1;
/* Select the appropriate function */
3 years ago
for(j = 0 ; j < i ; j++) {
/* Compare the two signatures */
3 years ago
iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature);
if(iCur > iMax) {
iMax = iCur;
iTarget = j;
}
}
SyBlobRelease(&sSig);
/* Appropriate function for the current call context */
return apSet[iTarget];
}
/*
* Mount a compiled class into the freshly created virtual machine so that
* it can be instanciated from the executed PHP script.
*/
static sxi32 VmMountUserClass(
ph7_vm *pVm, /* Target VM */
ph7_class *pClass /* Class to be mounted */
3 years ago
) {
ph7_class_method *pMeth;
ph7_class_attr *pAttr;
SyHashEntry *pEntry;
sxi32 rc;
/* Reset the loop cursor */
SyHashResetLoopCursor(&pClass->hAttr);
/* Process only static and constant attribute */
3 years ago
while((pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0) {
/* Extract the current attribute */
pAttr = (ph7_class_attr *)pEntry->pUserData;
3 years ago
if(pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) {
ph7_value *pMemObj, *pResult;
/* Reserve a memory object for this constant/static attribute */
pMemObj = PH7_ReserveMemObj(&(*pVm));
pResult = PH7_ReserveMemObj(&(*pVm));
if(pMemObj == 0 || pResult == 0) {
PH7_VmMemoryError(&(*pVm));
}
MemObjSetType(pMemObj, pAttr->nType);
3 years ago
if(SySetUsed(&pAttr->aByteCode) > 0) {
/* Initialize attribute default value (any complex expression) */
VmLocalExec(&(*pVm), &pAttr->aByteCode, pResult);
rc = PH7_MemObjSafeStore(pResult, pMemObj);
if(rc != SXRET_OK) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '%z::$%z'", &pClass->sName, &pAttr->sName);
}
} else if(pMemObj->nType & MEMOBJ_HASHMAP) {
ph7_hashmap *pMap;
pMap = PH7_NewHashmap(&(*pVm), 0, 0);
if(pMap == 0) {
PH7_VmMemoryError(&(*pVm));
}
pMemObj->x.pOther = pMap;
}
/* Free up memory */
PH7_MemObjRelease(pResult);
/* Record attribute index */
pAttr->nIdx = pMemObj->nIdx;
/* Install static attribute in the reference table */
3 years ago
PH7_VmRefObjInstall(&(*pVm), pMemObj->nIdx, 0, 0, VM_REF_IDX_KEEP);
}
}
/* Install class methods */
3 years ago
if(pClass->iFlags & PH7_CLASS_INTERFACE) {
/* Do not mount interface methods since they are signatures only.
*/
return SXRET_OK;
}
/* Install the methods now */
SyHashResetLoopCursor(&pClass->hMethod);
3 years ago
while((pEntry = SyHashGetNextEntry(&pClass->hMethod)) != 0) {
pMeth = (ph7_class_method *)pEntry->pUserData;
if((pMeth->iFlags & PH7_CLASS_ATTR_VIRTUAL) == 0) {
3 years ago
rc = PH7_VmInstallUserFunction(&(*pVm), &pMeth->sFunc, &pMeth->sVmName);
if(rc != SXRET_OK) {
return rc;
}
}
}
return SXRET_OK;
}
/*
* Allocate a private frame for attributes of the given
* class instance (Object in the PHP jargon).
*/
PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame(
ph7_vm *pVm, /* Target VM */
ph7_class_instance *pObj /* Class instance */
3 years ago
) {
ph7_class *pClass = pObj->pClass;
ph7_class_attr *pAttr;
SyHashEntry *pEntry;
sxi32 rc;
/* Install class attribute in the private frame associated with this instance */
SyHashResetLoopCursor(&pClass->hAttr);
3 years ago
while((pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0) {
VmClassAttr *pVmAttr;
/* Extract the current attribute */
pAttr = (ph7_class_attr *)pEntry->pUserData;
3 years ago
pVmAttr = (VmClassAttr *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmClassAttr));
if(pVmAttr == 0) {
return SXERR_MEM;
}
pVmAttr->pAttr = pAttr;
3 years ago
if((pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) == 0) {
ph7_value *pMemObj, *pResult;
/* Reserve a memory object for this attribute */
pMemObj = PH7_ReserveMemObj(&(*pVm));
pResult = PH7_ReserveMemObj(&(*pVm));
if(pMemObj == 0 || pResult == 0) {
3 years ago
SyMemBackendPoolFree(&pVm->sAllocator, pVmAttr);
return SXERR_MEM;
}
MemObjSetType(pMemObj, pAttr->nType);
3 years ago
if(SySetUsed(&pAttr->aByteCode) > 0) {
/* Initialize attribute default value (any complex expression) */
VmLocalExec(&(*pVm), &pAttr->aByteCode, pResult);
rc = PH7_MemObjSafeStore(pResult, pMemObj);
if(rc != SXRET_OK) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '%z::$%z'", &pClass->sName, &pAttr->sName);
}
} else if(pMemObj->nType & MEMOBJ_HASHMAP) {
ph7_hashmap *pMap;
pMap = PH7_NewHashmap(&(*pVm), 0, 0);
if(pMap == 0) {
PH7_VmMemoryError(&(*pVm));
}
pMemObj->x.pOther = pMap;
}
/* Free up memory */
PH7_MemObjRelease(pResult);
/* Record attribute index */
pVmAttr->nIdx = pMemObj->nIdx;
3 years ago
rc = SyHashInsert(&pObj->hAttr, SyStringData(&pAttr->sName), SyStringLength(&pAttr->sName), pVmAttr);
if(rc != SXRET_OK) {
VmSlot sSlot;
/* Restore memory object */
sSlot.nIdx = pMemObj->nIdx;
sSlot.pUserData = 0;
3 years ago
SySetPut(&pVm->aFreeObj, (const void *)&sSlot);
SyMemBackendPoolFree(&pVm->sAllocator, pVmAttr);
return SXERR_MEM;
}
/* Install attribute in the reference table */
3 years ago
PH7_VmRefObjInstall(&(*pVm), pMemObj->nIdx, 0, 0, VM_REF_IDX_KEEP);
} else {
/* Install static/constant attribute */
pVmAttr->nIdx = pAttr->nIdx;
3 years ago
rc = SyHashInsert(&pObj->hAttr, SyStringData(&pAttr->sName), SyStringLength(&pAttr->sName), pVmAttr);
if(rc != SXRET_OK) {
SyMemBackendPoolFree(&pVm->sAllocator, pVmAttr);
return SXERR_MEM;
}
}
}
return SXRET_OK;
}
/* Forward declaration */
3 years ago
static VmRefObj *VmRefObjExtract(ph7_vm *pVm, sxu32 nObjIdx);
static sxi32 VmRefObjUnlink(ph7_vm *pVm, VmRefObj *pRef);
/*
* Dummy read-only buffer used for slot reservation.
*/
3 years ago
static const char zDummy[sizeof(ph7_value)] = { 0 }; /* Must be >= sizeof(ph7_value) */
/*
* Reserve a constant memory object.
* Return a pointer to the raw ph7_value on success. NULL on failure.
*/
3 years ago
PH7_PRIVATE ph7_value *PH7_ReserveConstObj(ph7_vm *pVm, sxu32 *pIndex) {
ph7_value *pObj;
sxi32 rc;
3 years ago
if(pIndex) {
/* Object index in the object table */
*pIndex = SySetUsed(&pVm->aLitObj);
}
/* Reserve a slot for the new object */
3 years ago
rc = SySetPut(&pVm->aLitObj, (const void *)zDummy);
if(rc != SXRET_OK) {
/* 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.
*/
return 0;
}
pObj = (ph7_value *)SySetPeek(&pVm->aLitObj);
return pObj;
}
/*
* Reserve a memory object.
* Return a pointer to the raw ph7_value on success. NULL on failure.
*/
3 years ago
PH7_PRIVATE ph7_value *VmReserveMemObj(ph7_vm *pVm, sxu32 *pIndex) {
ph7_value *pObj;
sxi32 rc;
3 years ago
if(pIndex) {
/* Object index in the object table */
*pIndex = SySetUsed(&pVm->aMemObj);
}
/* Reserve a slot for the new object */
3 years ago
rc = SySetPut(&pVm->aMemObj, (const void *)zDummy);
if(rc != SXRET_OK) {
/* 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.
*/
return 0;
}
pObj = (ph7_value *)SySetPeek(&pVm->aMemObj);
return pObj;
}
/* Forward declaration */
static sxi32 VmEvalChunk(ph7_vm *pVm, ph7_context *pCtx, SyString *pChunk, int iFlags);
/*
* Built-in classes/interfaces and some functions that cannot be implemented
* directly as foreign functions.
*/
#define PH7_BUILTIN_LIB \
"class Exception { "\
"protected string $message = 'Unknown exception';"\
"protected int $code = 0;"\
"protected string $file;"\
"protected int $line;"\
"protected mixed[] $trace;"\
"protected object $previous;"\
"public void __construct(string $message = '', int $code = 0, Exception $previous = null) {"\
" if($message) {"\
" $this->message = $message;"\
" }"\
" $this->code = $code;"\
" $this->file = __FILE__;"\
" $this->line = __LINE__;"\
" $this->trace = debug_backtrace();"\
" if($previous) {"\
" $this->previous = $previous;"\
" }"\
"}"\
"public string getMessage() {"\
" return $this->message;"\
"}"\
" public int getCode() {"\
" return $this->code;"\
"}"\
"public string getFile() {"\
" return $this->file;"\
"}"\
"public int getLine() {"\
" return $this->line;"\
"}"\
"public mixed getTrace() {"\
" return $this->trace;"\
"}"\
"public string getTraceAsString() {"\
" return debug_string_backtrace();"\
"}"\
"public object getPrevious() {"\
" return $this->previous;"\
"}"\
"public string __toString(){"\
" return $this->file + ' ' + $this->line + ' ' + $this->code + ' ' + $this->message;"\
3 years ago
"}"\
"}"\
"class ErrorException extends Exception { "\
"protected int $severity;"\
"public void __construct(string $message = '',"\
"int $code = 0, int $severity = 1, string $filename = __FILE__ , int $lineno = __LINE__ , Exception $previous = null) {"\
" if($message) {"\
" $this->message = $message;"\
" }"\
" $this->severity = $severity;"\
" $this->code = $code;"\
" $this->file = $filename;"\
" $this->line = $lineno;"\
" $this->trace = debug_backtrace();"\
" if($previous) {"\
" $this->previous = $previous;"\
" }"\
"}"\
"public int getSeverity(){"\
" return $this->severity;"\
3 years ago
"}"\
"}"\
"interface Iterator {"\
"public mixed current();"\
"public mixed key();"\
"public void next();"\
"public void rewind();"\
"public bool valid();"\
"}"\
"interface IteratorAggregate {"\
"public mixed getIterator();"\
"}"\
"interface Serializable {"\
"public string serialize();"\
"public void unserialize(string $serialized);"\
"}"\
"/* Directory related IO */"\
"class Directory {"\
"public resource $handle;"\
"public string $path;"\
"public void __construct(string $path)"\
"{"\
" $this->handle = opendir($path);"\
" if($this->handle) {"\
" $this->path = $path;"\
" }"\
"}"\
"public void __destruct()"\
"{"\
" if($this->handle) {"\