From 91601deb844848dc02959679fd41e1441a76aff4 Mon Sep 17 00:00:00 2001 From: Aaron Seigo Date: Sun, 14 Dec 2014 12:30:27 +0100 Subject: the all-in-one-file version of unqlite --- common/unqlite/jx9_vm.c | 7141 ----------------------------------------------- 1 file changed, 7141 deletions(-) delete mode 100644 common/unqlite/jx9_vm.c (limited to 'common/unqlite/jx9_vm.c') diff --git a/common/unqlite/jx9_vm.c b/common/unqlite/jx9_vm.c deleted file mode 100644 index 1585b13..0000000 --- a/common/unqlite/jx9_vm.c +++ /dev/null @@ -1,7141 +0,0 @@ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * 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://jx9.symisc.net/ - */ - /* $SymiscID: jx9_vm.c v1.0 FreeBSD 2012-12-09 00:19 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* - * The code in this file implements execution method of the JX9 Virtual Machine. - * The JX9 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program - * which is then executed by the virtual machine implemented here to do the work of the JX9 - * statements. - * JX9 bytecode programs are similar in form to assembly language. The program consists - * of a linear sequence of operations .Each operation has an opcode and 3 operands. - * Operands P1 and P2 are integers where the first is signed while the second is unsigned. - * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually - * the jump destination used by the OP_JMP, OP_JZ, OP_JNZ, ... instructions. - * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands. - * Computation results are stored on a stack. Each entry on the stack is of type jx9_value. - * JX9 uses the jx9_value object to represent all values that can be stored in a JX9 variable. - * Since JX9 uses dynamic typing for the values it stores. Values stored in jx9_value objects - * can be integers, floating point values, strings, arrays, object instances (object in the JX9 jargon) - * and so on. - * Internally, the JX9 virtual machine manipulates nearly all values as jx9_values structures. - * Each jx9_value may cache multiple representations(string, integer etc.) of the same value. - * An implicit conversion from one type to the other occurs as necessary. - * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does - * the work of interpreting a JX9 bytecode program. But other routines are also provided - * to help in building up a program instruction by instruction. - */ -/* - * Each active virtual machine frame is represented by an instance - * of the following structure. - * VM Frame hold local variables and other stuff related to function call. - */ -struct VmFrame -{ - VmFrame *pParent; /* Parent frame or NULL if global scope */ - void *pUserData; /* Upper layer private data associated with this frame */ - SySet sLocal; /* Local variables container (VmSlot instance) */ - jx9_vm *pVm; /* VM that own this frame */ - SyHash hVar; /* Variable hashtable for fast lookup */ - SySet sArg; /* Function arguments container */ - sxi32 iFlags; /* Frame configuration flags (See below)*/ - sxu32 iExceptionJump; /* Exception jump destination */ -}; -/* - * When a user defined variable is garbage collected, memory object index - * is stored in an instance of the following structure and put in the free object - * table so that it can be reused again without allocating a new memory object. - */ -typedef struct VmSlot VmSlot; -struct VmSlot -{ - sxu32 nIdx; /* Index in pVm->aMemObj[] */ - void *pUserData; /* Upper-layer private data */ -}; -/* - * 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; -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 */ -}; -/* - * An instance of the following structure is used to record all MIME headers seen - * 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. - */ -typedef struct SyhttpHeader SyhttpHeader; -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_OTHR 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 JX9 program. - * The constant expansion mechanism under JX9 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. - */ -JX9_PRIVATE sxi32 jx9VmRegisterConstant( - jx9_vm *pVm, /* Target VM */ - const SyString *pName, /* Constant name */ - ProcConstant xExpand, /* Constant expansion callback */ - void *pUserData /* Last argument to xExpand() */ - ) -{ - jx9_constant *pCons; - SyHashEntry *pEntry; - char *zDupName; - sxi32 rc; - pEntry = SyHashGet(&pVm->hConstant, (const void *)pName->zString, pName->nByte); - if( pEntry ){ - /* Overwrite the old definition and return immediately */ - pCons = (jx9_constant *)pEntry->pUserData; - pCons->xExpand = xExpand; - pCons->pUserData = pUserData; - return SXRET_OK; - } - /* Allocate a new constant instance */ - pCons = (jx9_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_constant)); - if( pCons == 0 ){ - return 0; - } - /* Duplicate constant name */ - zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); - if( zDupName == 0 ){ - SyMemBackendPoolFree(&pVm->sAllocator, pCons); - return 0; - } - /* Install the constant */ - SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte); - pCons->xExpand = xExpand; - pCons->pUserData = pUserData; - rc = SyHashInsert(&pVm->hConstant, (const void *)zDupName, SyStringLength(&pCons->sName), pCons); - if( rc != SXRET_OK ){ - SyMemBackendFree(&pVm->sAllocator, zDupName); - SyMemBackendPoolFree(&pVm->sAllocator, pCons); - return rc; - } - /* All done, constant can be invoked from JX9 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 jx9NewForeignFunction( - jx9_vm *pVm, /* Target VM */ - const SyString *pName, /* Foreign function name */ - ProcHostFunction xFunc, /* Foreign function implementation */ - void *pUserData, /* Foreign function private data */ - jx9_user_func **ppOut /* OUT: VM image of the foreign function */ - ) -{ - jx9_user_func *pFunc; - char *zDup; - /* Allocate a new user function */ - pFunc = (jx9_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_user_func)); - if( pFunc == 0 ){ - return SXERR_MEM; - } - /* Duplicate function name */ - zDup = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); - if( zDup == 0 ){ - SyMemBackendPoolFree(&pVm->sAllocator, pFunc); - return SXERR_MEM; - } - /* Zero the structure */ - SyZero(pFunc, sizeof(jx9_user_func)); - /* Initialize structure fields */ - SyStringInitFromBuf(&pFunc->sName, zDup, pName->nByte); - pFunc->pVm = pVm; - pFunc->xFunc = xFunc; - pFunc->pUserData = pUserData; - SySetInit(&pFunc->aAux, &pVm->sAllocator, sizeof(jx9_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 JX9 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. - */ -JX9_PRIVATE sxi32 jx9VmInstallForeignFunction( - jx9_vm *pVm, /* Target VM */ - const SyString *pName, /* Foreign function name */ - ProcHostFunction xFunc, /* Foreign function implementation */ - void *pUserData /* Foreign function private data */ - ) -{ - jx9_user_func *pFunc; - SyHashEntry *pEntry; - sxi32 rc; - /* Overwrite any previously registered function with the same name */ - pEntry = SyHashGet(&pVm->hHostFunction, pName->zString, pName->nByte); - if( pEntry ){ - pFunc = (jx9_user_func *)pEntry->pUserData; - pFunc->pUserData = pUserData; - pFunc->xFunc = xFunc; - SySetReset(&pFunc->aAux); - return SXRET_OK; - } - /* Create a new user function */ - rc = jx9NewForeignFunction(&(*pVm), &(*pName), xFunc, pUserData, &pFunc); - if( rc != SXRET_OK ){ - return rc; - } - /* Install the function in the corresponding hashtable */ - 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. - */ -JX9_PRIVATE sxi32 jx9VmInitFuncState( - jx9_vm *pVm, /* Target VM */ - jx9_vm_func *pFunc, /* Target Fucntion */ - const char *zName, /* Function name */ - sxu32 nByte, /* zName length */ - sxi32 iFlags, /* Configuration flags */ - void *pUserData /* Function private data */ - ) -{ - /* Zero the structure */ - SyZero(pFunc, sizeof(jx9_vm_func)); - /* Initialize structure fields */ - /* Arguments container */ - SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(jx9_vm_func_arg)); - /* Static variable container */ - SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(jx9_vm_func_static_var)); - /* Bytecode container */ - SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr)); - /* Preallocate some instruction slots */ - SySetAlloc(&pFunc->aByteCode, 0x10); - pFunc->iFlags = iFlags; - pFunc->pUserData = pUserData; - SyStringInitFromBuf(&pFunc->sName, zName, nByte); - return SXRET_OK; -} -/* - * Install a user defined function in the corresponding VM container. - */ -JX9_PRIVATE sxi32 jx9VmInstallUserFunction( - jx9_vm *pVm, /* Target VM */ - jx9_vm_func *pFunc, /* Target function */ - SyString *pName /* Function name */ - ) -{ - SyHashEntry *pEntry; - sxi32 rc; - if( pName == 0 ){ - /* Use the built-in name */ - pName = &pFunc->sName; - } - /* Check for duplicates (functions with the same name) first */ - pEntry = SyHashGet(&pVm->hFunction, pName->zString, pName->nByte); - if( pEntry ){ - jx9_vm_func *pLink = (jx9_vm_func *)pEntry->pUserData; - if( pLink != pFunc ){ - /* Link */ - pFunc->pNextName = pLink; - pEntry->pUserData = pFunc; - } - return SXRET_OK; - } - /* First time seen */ - pFunc->pNextName = 0; - rc = SyHashInsert(&pVm->hFunction, pName->zString, pName->nByte, pFunc); - return rc; -} -/* - * Instruction builder interface. - */ -JX9_PRIVATE sxi32 jx9VmEmitInstr( - jx9_vm *pVm, /* Target VM */ - sxi32 iOp, /* Operation to perform */ - sxi32 iP1, /* First operand */ - sxu32 iP2, /* Second operand */ - void *p3, /* Third operand */ - sxu32 *pIndex /* Instruction index. NULL otherwise */ - ) -{ - VmInstr sInstr; - sxi32 rc; - /* Fill the VM instruction */ - sInstr.iOp = (sxu8)iOp; - sInstr.iP1 = iP1; - sInstr.iP2 = iP2; - sInstr.p3 = p3; - if( pIndex ){ - /* Instruction index in the bytecode array */ - *pIndex = SySetUsed(pVm->pByteContainer); - } - /* Finally, record the instruction */ - rc = SySetPut(pVm->pByteContainer, (const void *)&sInstr); - if( rc != SXRET_OK ){ - jx9GenCompileError(&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. - */ -JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer) -{ - if( pContainer == 0 ){ - /* Point to the default container */ - pVm->pByteContainer = &pVm->aByteCode; - }else{ - /* Change container */ - pVm->pByteContainer = &(*pContainer); - } - return SXRET_OK; -} -/* - * Return the current bytecode container. - */ -JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm) -{ - return pVm->pByteContainer; -} -/* - * Extract the VM instruction rooted at nIndex. - */ -JX9_PRIVATE VmInstr * jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex) -{ - VmInstr *pInstr; - pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex); - return pInstr; -} -/* - * Return the total number of VM instructions recorded so far. - */ -JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm) -{ - return SySetUsed(pVm->pByteContainer); -} -/* - * Pop the last VM instruction. - */ -JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm) -{ - return (VmInstr *)SySetPop(pVm->pByteContainer); -} -/* - * Peek the last VM instruction. - */ -JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm) -{ - return (VmInstr *)SySetPeek(pVm->pByteContainer); -} -/* - * Allocate a new virtual machine frame. - */ -static VmFrame * VmNewFrame( - jx9_vm *pVm, /* Target VM */ - void *pUserData /* Upper-layer private data */ - ) -{ - VmFrame *pFrame; - /* Allocate a new vm frame */ - pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmFrame)); - if( pFrame == 0 ){ - return 0; - } - /* Zero the structure */ - SyZero(pFrame, sizeof(VmFrame)); - /* Initialize frame fields */ - pFrame->pUserData = pUserData; - pFrame->pVm = pVm; - SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0); - SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot)); - SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot)); - return pFrame; -} -/* - * Enter a VM frame. - */ -static sxi32 VmEnterFrame( - jx9_vm *pVm, /* Target VM */ - void *pUserData, /* Upper-layer private data */ - VmFrame **ppFrame /* OUT: Top most active frame */ - ) -{ - VmFrame *pFrame; - /* Allocate a new frame */ - pFrame = VmNewFrame(&(*pVm), pUserData); - if( pFrame == 0 ){ - return SXERR_MEM; - } - /* Link to the list of active VM frame */ - pFrame->pParent = pVm->pFrame; - pVm->pFrame = pFrame; - if( ppFrame ){ - /* Write a pointer to the new VM frame */ - *ppFrame = pFrame; - } - return SXRET_OK; -} -/* - * Link a foreign variable with the TOP most active frame. - * Refer to the JX9_OP_UPLINK instruction implementation for more - * information. - */ -static sxi32 VmFrameLink(jx9_vm *pVm,SyString *pName) -{ - VmFrame *pTarget, *pFrame; - SyHashEntry *pEntry = 0; - sxi32 rc; - /* Point to the upper frame */ - pFrame = pVm->pFrame; - pTarget = pFrame; - pFrame = pTarget->pParent; - while( pFrame ){ - /* Query the current frame */ - pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte); - if( pEntry ){ - /* Variable found */ - break; - } - /* Point to the upper frame */ - pFrame = pFrame->pParent; - } - if( pEntry == 0 ){ - /* Inexistant variable */ - return SXERR_NOTFOUND; - } - /* Link to the current frame */ - rc = SyHashInsert(&pTarget->hVar, pEntry->pKey, pEntry->nKeyLen, pEntry->pUserData); - return rc; -} -/* - * Leave the top-most active frame. - */ -static void VmLeaveFrame(jx9_vm *pVm) -{ - VmFrame *pFrame = pVm->pFrame; - if( pFrame ){ - /* Unlink from the list of active VM frame */ - pVm->pFrame = pFrame->pParent; - if( pFrame->pParent ){ - VmSlot *aSlot; - sxu32 n; - /* Restore local variable to the free pool so that they can be reused again */ - aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal); - for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){ - /* Unset the local variable */ - jx9VmUnsetMemObj(&(*pVm), aSlot[n].nIdx); - } - } - /* Release internal containers */ - SyHashRelease(&pFrame->hVar); - SySetRelease(&pFrame->sArg); - SySetRelease(&pFrame->sLocal); - /* Release the whole structure */ - SyMemBackendPoolFree(&pVm->sAllocator, pFrame); - } -} -/* - * Compare two functions signature and return the comparison result. - */ -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; - for(;;){ - if( zFin >= zFend || zSin >= zSend ){ - break; - } - if( zFin[0] != zSin[0] ){ - /* mismatch */ - break; - } - zFin++; - zSin++; - } - return (int)(zFin-zPtr); -} -/* - * 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 JX9 engine. - * Refer to the official documentation for more information. - */ -static jx9_vm_func * VmOverload( - jx9_vm *pVm, /* Target VM */ - jx9_vm_func *pList, /* Linked list of candidates for overloading */ - jx9_value *aArg, /* Array of passed arguments */ - int nArg /* Total number of passed arguments */ - ) -{ - int iTarget, i, j, iCur, iMax; - jx9_vm_func *apSet[10]; /* Maximum number of candidates */ - jx9_vm_func *pLink; - SyString sArgSig; - SyBlob sSig; - - pLink = pList; - i = 0; - /* Put functions expecting the same number of passed arguments */ - while( i < (int)SX_ARRAYSIZE(apSet) ){ - if( pLink == 0 ){ - break; - } - if( (int)SySetUsed(&pLink->aArgs) == nArg ){ - /* Candidate for overloading */ - apSet[i++] = pLink; - } - /* Point to the next entry */ - pLink = pLink->pNextName; - } - if( i < 1 ){ - /* No candidates, return the head of the list */ - return pList; - } - if( nArg < 1 || i < 2 ){ - /* Return the only candidate */ - return apSet[0]; - } - /* Calculate function signature */ - SyBlobInit(&sSig, &pVm->sAllocator); - for( j = 0 ; j < nArg ; j++ ){ - int c = 'n'; /* null */ - if( aArg[j].iFlags & MEMOBJ_HASHMAP ){ - /* Hashmap */ - c = 'h'; - }else if( aArg[j].iFlags & MEMOBJ_BOOL ){ - /* bool */ - c = 'b'; - }else if( aArg[j].iFlags & MEMOBJ_INT ){ - /* int */ - c = 'i'; - }else if( aArg[j].iFlags & MEMOBJ_STRING ){ - /* String */ - c = 's'; - }else if( aArg[j].iFlags & MEMOBJ_REAL ){ - /* Float */ - c = 'f'; - } - if( c > 0 ){ - SyBlobAppend(&sSig, (const void *)&c, sizeof(char)); - } - } - SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig)); - iTarget = 0; - iMax = -1; - /* Select the appropriate function */ - for( j = 0 ; j < i ; j++ ){ - /* Compare the two signatures */ - 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]; -} -/* - * Dummy read-only buffer used for slot reservation. - */ -static const char zDummy[sizeof(jx9_value)] = { 0 }; /* Must be >= sizeof(jx9_value) */ -/* - * Reserve a constant memory object. - * Return a pointer to the raw jx9_value on success. NULL on failure. - */ -JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex) -{ - jx9_value *pObj; - sxi32 rc; - if( pIndex ){ - /* Object index in the object table */ - *pIndex = SySetUsed(&pVm->aLitObj); - } - /* Reserve a slot for the new object */ - 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 = (jx9_value *)SySetPeek(&pVm->aLitObj); - return pObj; -} -/* - * Reserve a memory object. - * Return a pointer to the raw jx9_value on success. NULL on failure. - */ -static jx9_value * VmReserveMemObj(jx9_vm *pVm, sxu32 *pIndex) -{ - jx9_value *pObj; - sxi32 rc; - if( pIndex ){ - /* Object index in the object table */ - *pIndex = SySetUsed(&pVm->aMemObj); - } - /* Reserve a slot for the new object */ - 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 = (jx9_value *)SySetPeek(&pVm->aMemObj); - return pObj; -} -/* Forward declaration */ -static sxi32 VmEvalChunk(jx9_vm *pVm, jx9_context *pCtx, SyString *pChunk, int iFlags, int bTrueReturn); -/* - * Built-in functions that cannot be implemented directly as foreign functions. - */ -#define JX9_BUILTIN_LIB \ - "function scandir(string $directory, int $sort_order = SCANDIR_SORT_ASCENDING)"\ - "{"\ - " if( func_num_args() < 1 ){ return FALSE; }"\ - " $aDir = [];"\ - " $pHandle = opendir($directory);"\ - " if( $pHandle == FALSE ){ return FALSE; }"\ - " while(FALSE !== ($pEntry = readdir($pHandle)) ){"\ - " $aDir[] = $pEntry;"\ - " }"\ - " closedir($pHandle);"\ - " if( $sort_order == SCANDIR_SORT_DESCENDING ){"\ - " rsort($aDir);"\ - " }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\ - " sort($aDir);"\ - " }"\ - " return $aDir;"\ - "}"\ - "function glob(string $pattern, int $iFlags = 0){"\ - "/* Open the target directory */"\ - "$zDir = dirname($pattern);"\ - "if(!is_string($zDir) ){ $zDir = './'; }"\ - "$pHandle = opendir($zDir);"\ - "if( $pHandle == FALSE ){"\ - " /* IO error while opening the current directory, return FALSE */"\ - " return FALSE;"\ - "}"\ - "$pattern = basename($pattern);"\ - "$pArray = []; /* Empty array */"\ - "/* Loop throw available entries */"\ - "while( FALSE !== ($pEntry = readdir($pHandle)) ){"\ - " /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\ - " $rc = strglob($pattern, $pEntry);"\ - " if( $rc ){"\ - " if( is_dir($pEntry) ){"\ - " if( $iFlags & GLOB_MARK ){"\ - " /* Adds a slash to each directory returned */"\ - " $pEntry .= DIRECTORY_SEPARATOR;"\ - " }"\ - " }else if( $iFlags & GLOB_ONLYDIR ){"\ - " /* Not a directory, ignore */"\ - " continue;"\ - " }"\ - " /* Add the entry */"\ - " $pArray[] = $pEntry;"\ - " }"\ - " }"\ - "/* Close the handle */"\ - "closedir($pHandle);"\ - "if( ($iFlags & GLOB_NOSORT) == 0 ){"\ - " /* Sort the array */"\ - " sort($pArray);"\ - "}"\ - "if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\ - " /* Return the search pattern if no files matching were found */"\ - " $pArray[] = $pattern;"\ - "}"\ - "/* Return the created array */"\ - "return $pArray;"\ - "}"\ - "/* Creates a temporary file */"\ - "function tmpfile(){"\ - " /* Extract the temp directory */"\ - " $zTempDir = sys_get_temp_dir();"\ - " if( strlen($zTempDir) < 1 ){"\ - " /* Use the current dir */"\ - " $zTempDir = '.';"\ - " }"\ - " /* Create the file */"\ - " $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'JX9'.rand_str(12), 'w+');"\ - " return $pHandle;"\ - "}"\ - "/* Creates a temporary filename */"\ - "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */, string $zPrefix = 'JX9')"\ - "{"\ - " return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\ - "}"\ - "function max(){"\ - " $pArgs = func_get_args();"\ - " if( sizeof($pArgs) < 1 ){"\ - " return null;"\ - " }"\ - " if( sizeof($pArgs) < 2 ){"\ - " $pArg = $pArgs[0];"\ - " if( !is_array($pArg) ){"\ - " return $pArg; "\ - " }"\ - " if( sizeof($pArg) < 1 ){"\ - " return null;"\ - " }"\ - " $pArg = array_copy($pArgs[0]);"\ - " reset($pArg);"\ - " $max = current($pArg);"\ - " while( FALSE !== ($val = next($pArg)) ){"\ - " if( $val > $max ){"\ - " $max = $val;"\ - " }"\ - " }"\ - " return $max;"\ - " }"\ - " $max = $pArgs[0];"\ - " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\ - " $val = $pArgs[$i];"\ - "if( $val > $max ){"\ - " $max = $val;"\ - "}"\ - " }"\ - " return $max;"\ - "}"\ - "function min(){"\ - " $pArgs = func_get_args();"\ - " if( sizeof($pArgs) < 1 ){"\ - " return null;"\ - " }"\ - " if( sizeof($pArgs) < 2 ){"\ - " $pArg = $pArgs[0];"\ - " if( !is_array($pArg) ){"\ - " return $pArg; "\ - " }"\ - " if( sizeof($pArg) < 1 ){"\ - " return null;"\ - " }"\ - " $pArg = array_copy($pArgs[0]);"\ - " reset($pArg);"\ - " $min = current($pArg);"\ - " while( FALSE !== ($val = next($pArg)) ){"\ - " if( $val < $min ){"\ - " $min = $val;"\ - " }"\ - " }"\ - " return $min;"\ - " }"\ - " $min = $pArgs[0];"\ - " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\ - " $val = $pArgs[$i];"\ - "if( $val < $min ){"\ - " $min = $val;"\ - " }"\ - " }"\ - " return $min;"\ - "}" -/* - * Initialize a freshly allocated JX9 Virtual Machine so that we can - * start compiling the target JX9 program. - */ -JX9_PRIVATE sxi32 jx9VmInit( - jx9_vm *pVm, /* Initialize this */ - jx9 *pEngine /* Master engine */ - ) -{ - SyString sBuiltin; - jx9_value *pObj; - sxi32 rc; - /* Zero the structure */ - SyZero(pVm, sizeof(jx9_vm)); - /* Initialize VM fields */ - pVm->pEngine = &(*pEngine); - SyMemBackendInitFromParent(&pVm->sAllocator, &pEngine->sAllocator); - /* Instructions containers */ - SySetInit(&pVm->aByteCode, &pVm->sAllocator, sizeof(VmInstr)); - SySetAlloc(&pVm->aByteCode, 0xFF); - pVm->pByteContainer = &pVm->aByteCode; - /* Object containers */ - SySetInit(&pVm->aMemObj, &pVm->sAllocator, sizeof(jx9_value)); - SySetAlloc(&pVm->aMemObj, 0xFF); - /* Virtual machine internal containers */ - SyBlobInit(&pVm->sConsumer, &pVm->sAllocator); - SyBlobInit(&pVm->sWorker, &pVm->sAllocator); - SyBlobInit(&pVm->sArgv, &pVm->sAllocator); - SySetInit(&pVm->aLitObj, &pVm->sAllocator, sizeof(jx9_value)); - SySetAlloc(&pVm->aLitObj, 0xFF); - SyHashInit(&pVm->hHostFunction, &pVm->sAllocator, 0, 0); - SyHashInit(&pVm->hFunction, &pVm->sAllocator, 0, 0); - SyHashInit(&pVm->hConstant, &pVm->sAllocator, 0, 0); - SyHashInit(&pVm->hSuper, &pVm->sAllocator, 0, 0); - SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot)); - /* Configuration containers */ - SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString)); - SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString)); - SySetInit(&pVm->aIncluded, &pVm->sAllocator, sizeof(SyString)); - SySetInit(&pVm->aIOstream, &pVm->sAllocator, sizeof(jx9_io_stream *)); - /* Error callbacks containers */ - jx9MemObjInit(&(*pVm), &pVm->sAssertCallback); - /* Set a default recursion limit */ -#if defined(__WINNT__) || defined(__UNIXES__) - pVm->nMaxDepth = 32; -#else - pVm->nMaxDepth = 16; -#endif - /* Default assertion flags */ - pVm->iAssertFlags = JX9_ASSERT_WARNING; /* Issue a warning for each failed assertion */ - /* PRNG context */ - SyRandomnessInit(&pVm->sPrng, 0, 0); - /* Install the null constant */ - pObj = jx9VmReserveConstObj(&(*pVm), 0); - if( pObj == 0 ){ - rc = SXERR_MEM; - goto Err; - } - jx9MemObjInit(pVm, pObj); - /* Install the boolean TRUE constant */ - pObj = jx9VmReserveConstObj(&(*pVm), 0); - if( pObj == 0 ){ - rc = SXERR_MEM; - goto Err; - } - jx9MemObjInitFromBool(pVm, pObj, 1); - /* Install the boolean FALSE constant */ - pObj = jx9VmReserveConstObj(&(*pVm), 0); - if( pObj == 0 ){ - rc = SXERR_MEM; - goto Err; - } - jx9MemObjInitFromBool(pVm, pObj, 0); - /* Create the global frame */ - rc = VmEnterFrame(&(*pVm), 0, 0); - if( rc != SXRET_OK ){ - goto Err; - } - /* Initialize the code generator */ - rc = jx9InitCodeGenerator(pVm, pEngine->xConf.xErr, pEngine->xConf.pErrData); - if( rc != SXRET_OK ){ - goto Err; - } - /* VM correctly initialized, set the magic number */ - pVm->nMagic = JX9_VM_INIT; - SyStringInitFromBuf(&sBuiltin,JX9_BUILTIN_LIB, sizeof(JX9_BUILTIN_LIB)-1); - /* Compile the built-in library */ - VmEvalChunk(&(*pVm), 0, &sBuiltin, 0, FALSE); - /* Reset the code generator */ - jx9ResetCodeGenerator(&(*pVm), pEngine->xConf.xErr, pEngine->xConf.pErrData); - return SXRET_OK; -Err: - SyMemBackendRelease(&pVm->sAllocator); - return rc; -} -/* - * Default VM output consumer callback.That is, all VM output is redirected to this - * routine which store the output in an internal blob. - * The output can be extracted later after program execution [jx9_vm_exec()] via - * the [jx9_vm_config()] interface with a configuration verb set to - * jx9VM_CONFIG_EXTRACT_OUTPUT. - * Refer to the official docurmentation for additional information. - * Note that for performance reason it's preferable to install a VM output - * consumer callback via (jx9VM_CONFIG_OUTPUT) rather than waiting for the VM - * to finish executing and extracting the output. - */ -JX9_PRIVATE sxi32 jx9VmBlobConsumer( - const void *pOut, /* VM Generated output*/ - unsigned int nLen, /* Generated output length */ - void *pUserData /* User private data */ - ) -{ - sxi32 rc; - /* Store the output in an internal BLOB */ - rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen); - return rc; -} -#define VM_STACK_GUARD 16 -/* - * Allocate a new operand stack so that we can start executing - * our compiled JX9 program. - * Return a pointer to the operand stack (array of jx9_values) - * on success. NULL (Fatal error) on failure. - */ -static jx9_value * VmNewOperandStack( - jx9_vm *pVm, /* Target VM */ - sxu32 nInstr /* Total numer of generated bytecode instructions */ - ) -{ - jx9_value *pStack; - /* No instruction ever pushes more than a single element onto the - ** stack and the stack never grows on successive executions of the - ** same loop. So the total number of instructions is an upper bound - ** on the maximum stack depth required. - ** - ** Allocation all the stack space we will ever need. - */ - nInstr += VM_STACK_GUARD; - pStack = (jx9_value *)SyMemBackendAlloc(&pVm->sAllocator, nInstr * sizeof(jx9_value)); - if( pStack == 0 ){ - return 0; - } - /* Initialize the operand stack */ - while( nInstr > 0 ){ - jx9MemObjInit(&(*pVm), &pStack[nInstr - 1]); - --nInstr; - } - /* Ready for bytecode execution */ - return pStack; -} -/* Forward declaration */ -static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm); -/* - * Prepare the Virtual Machine for bytecode execution. - * This routine gets called by the JX9 engine after - * successful compilation of the target JX9 program. - */ -JX9_PRIVATE sxi32 jx9VmMakeReady( - jx9_vm *pVm /* Target VM */ - ) -{ - sxi32 rc; - if( pVm->nMagic != JX9_VM_INIT ){ - /* Initialize your VM first */ - return SXERR_CORRUPT; - } - /* Mark the VM ready for bytecode execution */ - pVm->nMagic = JX9_VM_RUN; - /* Release the code generator now we have compiled our program */ - jx9ResetCodeGenerator(pVm, 0, 0); - /* Emit the DONE instruction */ - rc = jx9VmEmitInstr(&(*pVm), JX9_OP_DONE, 0, 0, 0, 0); - if( rc != SXRET_OK ){ - return SXERR_MEM; - } - /* Script return value */ - jx9MemObjInit(&(*pVm), &pVm->sExec); /* Assume a NULL return value */ - /* Allocate a new operand stack */ - pVm->aOps = VmNewOperandStack(&(*pVm), SySetUsed(pVm->pByteContainer)); - if( pVm->aOps == 0 ){ - return SXERR_MEM; - } - /* Set the default VM output consumer callback and it's - * private data. */ - pVm->sVmConsumer.xConsumer = jx9VmBlobConsumer; - pVm->sVmConsumer.pUserData = &pVm->sConsumer; - /* Register special functions first [i.e: print, func_get_args(), die, etc.] */ - rc = VmRegisterSpecialFunction(&(*pVm)); - if( rc != SXRET_OK ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return rc; - } - /* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */ - rc = jx9HashmapLoadBuiltin(&(*pVm)); - if( rc != SXRET_OK ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return rc; - } - /* Register built-in constants [i.e: JX9_EOL, JX9_OS...] */ - jx9RegisterBuiltInConstant(&(*pVm)); - /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */ - jx9RegisterBuiltInFunction(&(*pVm)); - /* VM is ready for bytecode execution */ - return SXRET_OK; -} -/* - * Reset a Virtual Machine to it's initial state. - */ -JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm) -{ - if( pVm->nMagic != JX9_VM_RUN && pVm->nMagic != JX9_VM_EXEC ){ - return SXERR_CORRUPT; - } - /* TICKET 1433-003: As of this version, the VM is automatically reset */ - SyBlobReset(&pVm->sConsumer); - jx9MemObjRelease(&pVm->sExec); - /* Set the ready flag */ - pVm->nMagic = JX9_VM_RUN; - return SXRET_OK; -} -/* - * Release a Virtual Machine. - * Every virtual machine must be destroyed in order to avoid memory leaks. - */ -JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm) -{ - /* Set the stale magic number */ - pVm->nMagic = JX9_VM_STALE; - /* Release the private memory subsystem */ - SyMemBackendRelease(&pVm->sAllocator); - return SXRET_OK; -} -/* - * Initialize a foreign function call context. - * The context in which a foreign function executes is stored in a jx9_context object. - * A pointer to a jx9_context object is always first parameter to application-defined foreign - * functions. - * The application-defined foreign function implementation will pass this pointer through into - * calls to dozens of interfaces, these includes jx9_result_int(), jx9_result_string(), jx9_result_value(), - * jx9_context_new_scalar(), jx9_context_alloc_chunk(), jx9_context_output(), jx9_context_throw_error() - * and many more. Refer to the C/C++ Interfaces documentation for additional information. - */ -static sxi32 VmInitCallContext( - jx9_context *pOut, /* Call Context */ - jx9_vm *pVm, /* Target VM */ - jx9_user_func *pFunc, /* Foreign function to execute shortly */ - jx9_value *pRet, /* Store return value here*/ - sxi32 iFlags /* Control flags */ - ) -{ - pOut->pFunc = pFunc; - pOut->pVm = pVm; - SySetInit(&pOut->sVar, &pVm->sAllocator, sizeof(jx9_value *)); - SySetInit(&pOut->sChunk, &pVm->sAllocator, sizeof(jx9_aux_data)); - /* Assume a null return value */ - MemObjSetType(pRet, MEMOBJ_NULL); - pOut->pRet = pRet; - pOut->iFlags = iFlags; - return SXRET_OK; -} -/* - * Release a foreign function call context and cleanup the mess - * left behind. - */ -static void VmReleaseCallContext(jx9_context *pCtx) -{ - sxu32 n; - if( SySetUsed(&pCtx->sVar) > 0 ){ - jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar); - for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){ - if( apObj[n] == 0 ){ - /* Already released */ - continue; - } - jx9MemObjRelease(apObj[n]); - SyMemBackendPoolFree(&pCtx->pVm->sAllocator, apObj[n]); - } - SySetRelease(&pCtx->sVar); - } - if( SySetUsed(&pCtx->sChunk) > 0 ){ - jx9_aux_data *aAux; - void *pChunk; - /* Automatic release of dynamically allocated chunk - * using [jx9_context_alloc_chunk()]. - */ - aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk); - for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){ - pChunk = aAux[n].pAuxData; - /* Release the chunk */ - if( pChunk ){ - SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk); - } - } - SySetRelease(&pCtx->sChunk); - } -} -/* - * Release a jx9_value allocated from the body of a foreign function. - * Refer to [jx9_context_release_value()] for additional information. - */ -JX9_PRIVATE void jx9VmReleaseContextValue( - jx9_context *pCtx, /* Call context */ - jx9_value *pValue /* Release this value */ - ) -{ - if( pValue == 0 ){ - /* NULL value is a harmless operation */ - return; - } - if( SySetUsed(&pCtx->sVar) > 0 ){ - jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar); - sxu32 n; - for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){ - if( apObj[n] == pValue ){ - jx9MemObjRelease(pValue); - SyMemBackendPoolFree(&pCtx->pVm->sAllocator, pValue); - /* Mark as released */ - apObj[n] = 0; - break; - } - } - } -} -/* - * Pop and release as many memory object from the operand stack. - */ -static void VmPopOperand( - jx9_value **ppTos, /* Operand stack */ - sxi32 nPop /* Total number of memory objects to pop */ - ) -{ - jx9_value *pTos = *ppTos; - while( nPop > 0 ){ - jx9MemObjRelease(pTos); - pTos--; - nPop--; - } - /* Top of the stack */ - *ppTos = pTos; -} -/* - * Reserve a memory object. - * Return a pointer to the raw jx9_value on success. NULL on failure. - */ -JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIdx) -{ - jx9_value *pObj = 0; - VmSlot *pSlot; - sxu32 nIdx; - /* Check for a free slot */ - nIdx = SXU32_HIGH; /* cc warning */ - pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj); - if( pSlot ){ - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx); - nIdx = pSlot->nIdx; - } - if( pObj == 0 ){ - /* Reserve a new memory object */ - pObj = VmReserveMemObj(&(*pVm), &nIdx); - if( pObj == 0 ){ - return 0; - } - } - /* Set a null default value */ - jx9MemObjInit(&(*pVm), pObj); - if( pIdx ){ - *pIdx = nIdx; - } - pObj->nIdx = nIdx; - return pObj; -} -/* - * Extract a variable value from the top active VM frame. - * Return a pointer to the variable value on success. - * NULL otherwise (non-existent variable/Out-of-memory, ...). - */ -static jx9_value * VmExtractMemObj( - jx9_vm *pVm, /* Target VM */ - const SyString *pName, /* Variable name */ - int bDup, /* True to duplicate variable name */ - int bCreate /* True to create the variable if non-existent */ - ) -{ - int bNullify = FALSE; - SyHashEntry *pEntry; - VmFrame *pFrame; - jx9_value *pObj; - sxu32 nIdx; - sxi32 rc; - /* Point to the top active frame */ - pFrame = pVm->pFrame; - /* Perform the lookup */ - if( pName == 0 || pName->nByte < 1 ){ - static const SyString sAnnon = { " " , sizeof(char) }; - pName = &sAnnon; - /* Always nullify the object */ - bNullify = TRUE; - bDup = FALSE; - } - /* Check the superglobals table first */ - pEntry = SyHashGet(&pVm->hSuper, (const void *)pName->zString, pName->nByte); - if( pEntry == 0 ){ - /* Query the top active frame */ - pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte); - if( pEntry == 0 ){ - char *zName = (char *)pName->zString; - VmSlot sLocal; - if( !bCreate ){ - /* Do not create the variable, return NULL */ - return 0; - } - /* No such variable, automatically create a new one and install - * it in the current frame. - */ - pObj = jx9VmReserveMemObj(&(*pVm),&nIdx); - if( pObj == 0 ){ - return 0; - } - if( bDup ){ - /* Duplicate name */ - zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); - if( zName == 0 ){ - return 0; - } - } - /* Link to the top active VM frame */ - rc = SyHashInsert(&pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx)); - if( rc != SXRET_OK ){ - /* Return the slot to the free pool */ - sLocal.nIdx = nIdx; - sLocal.pUserData = 0; - SySetPut(&pVm->aFreeObj, (const void *)&sLocal); - return 0; - } - if( pFrame->pParent != 0 ){ - /* Local variable */ - sLocal.nIdx = nIdx; - SySetPut(&pFrame->sLocal, (const void *)&sLocal); - } - }else{ - /* Extract variable contents */ - nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - if( bNullify && pObj ){ - jx9MemObjRelease(pObj); - } - } - }else{ - /* Superglobal */ - nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - } - return pObj; -} -/* - * Extract a superglobal variable such as $_GET, $_POST, $_HEADERS, .... - * Return a pointer to the variable value on success.NULL otherwise. - */ -static jx9_value * VmExtractSuper( - jx9_vm *pVm, /* Target VM */ - const char *zName, /* Superglobal name: NOT NULL TERMINATED */ - sxu32 nByte /* zName length */ - ) -{ - SyHashEntry *pEntry; - jx9_value *pValue; - sxu32 nIdx; - /* Query the superglobal table */ - pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte); - if( pEntry == 0 ){ - /* No such entry */ - return 0; - } - /* Extract the superglobal index in the global object pool */ - nIdx = SX_PTR_TO_INT(pEntry->pUserData); - /* Extract the variable value */ - pValue = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - return pValue; -} -/* - * Perform a raw hashmap insertion. - * Refer to the [jx9VmConfigure()] implementation for additional information. - */ -static sxi32 VmHashmapInsert( - jx9_hashmap *pMap, /* Target hashmap */ - const char *zKey, /* Entry key */ - int nKeylen, /* zKey length*/ - const char *zData, /* Entry data */ - int nLen /* zData length */ - ) -{ - jx9_value sKey,sValue; - jx9_value *pKey; - sxi32 rc; - pKey = 0; - jx9MemObjInit(pMap->pVm, &sKey); - jx9MemObjInitFromString(pMap->pVm, &sValue, 0); - if( zKey ){ - if( nKeylen < 0 ){ - nKeylen = (int)SyStrlen(zKey); - } - jx9MemObjStringAppend(&sKey, zKey, (sxu32)nKeylen); - pKey = &sKey; - } - if( zData ){ - if( nLen < 0 ){ - /* Compute length automatically */ - nLen = (int)SyStrlen(zData); - } - jx9MemObjStringAppend(&sValue, zData, (sxu32)nLen); - } - /* Perform the insertion */ - rc = jx9HashmapInsert(&(*pMap),pKey,&sValue); - jx9MemObjRelease(&sKey); - jx9MemObjRelease(&sValue); - return rc; -} -/* Forward declaration */ -static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte); -/* - * Configure a working virtual machine instance. - * - * This routine is used to configure a JX9 virtual machine obtained by a prior - * successful call to one of the compile interface such as jx9_compile() - * jx9_compile_v2() or jx9_compile_file(). - * The second argument to this function is an integer configuration option - * that determines what property of the JX9 virtual machine is to be configured. - * Subsequent arguments vary depending on the configuration option in the second - * argument. There are many verbs but the most important are JX9_VM_CONFIG_OUTPUT, - * JX9_VM_CONFIG_HTTP_REQUEST and JX9_VM_CONFIG_ARGV_ENTRY. - * Refer to the official documentation for the list of allowed verbs. - */ -JX9_PRIVATE sxi32 jx9VmConfigure( - jx9_vm *pVm, /* Target VM */ - sxi32 nOp, /* Configuration verb */ - va_list ap /* Subsequent option arguments */ - ) -{ - sxi32 rc = SXRET_OK; - switch(nOp){ - case JX9_VM_CONFIG_OUTPUT: { - ProcConsumer xConsumer = va_arg(ap, ProcConsumer); - void *pUserData = va_arg(ap, void *); - /* VM output consumer callback */ -#ifdef UNTRUST - if( xConsumer == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - /* Install the output consumer */ - pVm->sVmConsumer.xConsumer = xConsumer; - pVm->sVmConsumer.pUserData = pUserData; - break; - } - case JX9_VM_CONFIG_IMPORT_PATH: { - /* Import path */ - const char *zPath; - SyString sPath; - zPath = va_arg(ap, const char *); -#if defined(UNTRUST) - if( zPath == 0 ){ - rc = SXERR_EMPTY; - break; - } -#endif - SyStringInitFromBuf(&sPath, zPath, SyStrlen(zPath)); - /* Remove trailing slashes and backslashes */ -#ifdef __WINNT__ - SyStringTrimTrailingChar(&sPath, '\\'); -#endif - SyStringTrimTrailingChar(&sPath, '/'); - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sPath); - if( sPath.nByte > 0 ){ - /* Store the path in the corresponding conatiner */ - rc = SySetPut(&pVm->aPaths, (const void *)&sPath); - } - break; - } - case JX9_VM_CONFIG_ERR_REPORT: - /* Run-Time Error report */ - pVm->bErrReport = 1; - break; - case JX9_VM_CONFIG_RECURSION_DEPTH:{ - /* Recursion depth */ - int nDepth = va_arg(ap, int); - if( nDepth > 2 && nDepth < 1024 ){ - pVm->nMaxDepth = nDepth; - } - break; - } - case JX9_VM_OUTPUT_LENGTH: { - /* VM output length in bytes */ - sxu32 *pOut = va_arg(ap, sxu32 *); -#ifdef UNTRUST - if( pOut == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - *pOut = pVm->nOutputLen; - break; - } - case JX9_VM_CONFIG_CREATE_VAR: { - /* Create a new superglobal/global variable */ - const char *zName = va_arg(ap, const char *); - jx9_value *pValue = va_arg(ap, jx9_value *); - SyHashEntry *pEntry; - jx9_value *pObj; - sxu32 nByte; - sxu32 nIdx; -#ifdef UNTRUST - if( SX_EMPTY_STR(zName) || pValue == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - nByte = SyStrlen(zName); - /* Check if the superglobal is already installed */ - pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte); - if( pEntry ){ - /* Variable already installed */ - nIdx = SX_PTR_TO_INT(pEntry->pUserData); - /* Extract contents */ - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - if( pObj ){ - /* Overwrite old contents */ - jx9MemObjStore(pValue, pObj); - } - }else{ - /* Install a new variable */ - pObj = jx9VmReserveMemObj(&(*pVm),&nIdx); - if( pObj == 0 ){ - rc = SXERR_MEM; - break; - } - /* Copy value */ - jx9MemObjStore(pValue, pObj); - /* Install the superglobal */ - rc = SyHashInsert(&pVm->hSuper, (const void *)zName, nByte, SX_INT_TO_PTR(nIdx)); - } - break; - } - case JX9_VM_CONFIG_SERVER_ATTR: - case JX9_VM_CONFIG_ENV_ATTR: { - const char *zKey = va_arg(ap, const char *); - const char *zValue = va_arg(ap, const char *); - int nLen = va_arg(ap, int); - jx9_hashmap *pMap; - jx9_value *pValue; - if( nOp == JX9_VM_CONFIG_ENV_ATTR ){ - /* Extract the $_ENV superglobal */ - pValue = VmExtractSuper(&(*pVm), "_ENV", sizeof("_ENV")-1); - }else{ - /* Extract the $_SERVER superglobal */ - pValue = VmExtractSuper(&(*pVm), "_SERVER", sizeof("_SERVER")-1); - } - if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* No such entry */ - rc = SXERR_NOTFOUND; - break; - } - /* Point to the hashmap */ - pMap = (jx9_hashmap *)pValue->x.pOther; - /* Perform the insertion */ - rc = VmHashmapInsert(pMap, zKey, -1, zValue, nLen); - break; - } - case JX9_VM_CONFIG_ARGV_ENTRY:{ - /* Script arguments */ - const char *zValue = va_arg(ap, const char *); - jx9_hashmap *pMap; - jx9_value *pValue; - /* Extract the $argv array */ - pValue = VmExtractSuper(&(*pVm), "argv", sizeof("argv")-1); - if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* No such entry */ - rc = SXERR_NOTFOUND; - break; - } - /* Point to the hashmap */ - pMap = (jx9_hashmap *)pValue->x.pOther; - /* Perform the insertion */ - rc = VmHashmapInsert(pMap, 0, 0, zValue,-1); - if( rc == SXRET_OK && zValue && zValue[0] != 0 ){ - if( pMap->nEntry > 1 ){ - /* Append space separator first */ - SyBlobAppend(&pVm->sArgv, (const void *)" ", sizeof(char)); - } - SyBlobAppend(&pVm->sArgv, (const void *)zValue,SyStrlen(zValue)); - } - break; - } - case JX9_VM_CONFIG_EXEC_VALUE: { - /* Script return value */ - jx9_value **ppValue = va_arg(ap, jx9_value **); -#ifdef UNTRUST - if( ppValue == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - *ppValue = &pVm->sExec; - break; - } - case JX9_VM_CONFIG_IO_STREAM: { - /* Register an IO stream device */ - const jx9_io_stream *pStream = va_arg(ap, const jx9_io_stream *); - /* Make sure we are dealing with a valid IO stream */ - if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 || - pStream->xOpen == 0 || pStream->xRead == 0 ){ - /* Invalid stream */ - rc = SXERR_INVALID; - break; - } - if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName, "file", sizeof("file")-1) == 0 ){ - /* Make the 'file://' stream the defaut stream device */ - pVm->pDefStream = pStream; - } - /* Insert in the appropriate container */ - rc = SySetPut(&pVm->aIOstream, (const void *)&pStream); - break; - } - case JX9_VM_CONFIG_EXTRACT_OUTPUT: { - /* Point to the VM internal output consumer buffer */ - const void **ppOut = va_arg(ap, const void **); - unsigned int *pLen = va_arg(ap, unsigned int *); -#ifdef UNTRUST - if( ppOut == 0 || pLen == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - *ppOut = SyBlobData(&pVm->sConsumer); - *pLen = SyBlobLength(&pVm->sConsumer); - break; - } - case JX9_VM_CONFIG_HTTP_REQUEST:{ - /* Raw HTTP request*/ - const char *zRequest = va_arg(ap, const char *); - int nByte = va_arg(ap, int); - if( SX_EMPTY_STR(zRequest) ){ - rc = SXERR_EMPTY; - break; - } - if( nByte < 0 ){ - /* Compute length automatically */ - nByte = (int)SyStrlen(zRequest); - } - /* Process the request */ - rc = VmHttpProcessRequest(&(*pVm), zRequest, nByte); - break; - } - default: - /* Unknown configuration option */ - rc = SXERR_UNKNOWN; - break; - } - return rc; -} -/* Forward declaration */ -static const char * VmInstrToString(sxi32 nOp); -/* - * This routine is used to dump JX9 bytecode instructions to a human readable - * format. - * The dump is redirected to the given consumer callback which is responsible - * of consuming the generated dump perhaps redirecting it to its standard output - * (STDOUT). - */ -static sxi32 VmByteCodeDump( - SySet *pByteCode, /* Bytecode container */ - ProcConsumer xConsumer, /* Dump consumer callback */ - void *pUserData /* Last argument to xConsumer() */ - ) -{ - static const char zDump[] = { - "====================================================\n" - "JX9 VM Dump Copyright (C) 2012-2013 Symisc Systems\n" - " http://jx9.symisc.net/\n" - "====================================================\n" - }; - VmInstr *pInstr, *pEnd; - sxi32 rc = SXRET_OK; - sxu32 n; - /* Point to the JX9 instructions */ - pInstr = (VmInstr *)SySetBasePtr(pByteCode); - pEnd = &pInstr[SySetUsed(pByteCode)]; - n = 0; - xConsumer((const void *)zDump, sizeof(zDump)-1, pUserData); - /* Dump instructions */ - for(;;){ - if( pInstr >= pEnd ){ - /* No more instructions */ - break; - } - /* Format and call the consumer callback */ - rc = SyProcFormat(xConsumer, pUserData, "%s %8d %8u %#8x [%u]\n", - VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2, - SX_PTR_TO_INT(pInstr->p3), n); - if( rc != SXRET_OK ){ - /* Consumer routine request an operation abort */ - return rc; - } - ++n; - pInstr++; /* Next instruction in the stream */ - } - return rc; -} -/* - * Consume a generated run-time error message by invoking the VM output - * consumer callback. - */ -static sxi32 VmCallErrorHandler(jx9_vm *pVm, SyBlob *pMsg) -{ - jx9_output_consumer *pCons = &pVm->sVmConsumer; - sxi32 rc = SXRET_OK; - /* Append a new line */ -#ifdef __WINNT__ - SyBlobAppend(pMsg, "\r\n", sizeof("\r\n")-1); -#else - SyBlobAppend(pMsg, "\n", sizeof(char)); -#endif - /* Invoke the output consumer callback */ - rc = pCons->xConsumer(SyBlobData(pMsg), SyBlobLength(pMsg), pCons->pUserData); - /* Increment output length */ - pVm->nOutputLen += SyBlobLength(pMsg); - - return rc; -} -/* - * Throw a run-time error and invoke the supplied VM output consumer callback. - * Refer to the implementation of [jx9_context_throw_error()] for additional - * information. - */ -JX9_PRIVATE sxi32 jx9VmThrowError( - jx9_vm *pVm, /* Target VM */ - SyString *pFuncName, /* Function name. NULL otherwise */ - sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice]*/ - const char *zMessage /* Null terminated error message */ - ) -{ - SyBlob *pWorker = &pVm->sWorker; - SyString *pFile; - char *zErr; - sxi32 rc; - if( !pVm->bErrReport ){ - /* Don't bother reporting errors */ - return SXRET_OK; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - /* Peek the processed file if available */ - pFile = (SyString *)SySetPeek(&pVm->aFiles); - if( pFile ){ - /* Append file name */ - SyBlobAppend(pWorker, pFile->zString, pFile->nByte); - SyBlobAppend(pWorker, (const void *)" ", sizeof(char)); - } - zErr = "Error: "; - switch(iErr){ - case JX9_CTX_WARNING: zErr = "Warning: "; break; - case JX9_CTX_NOTICE: zErr = "Notice: "; break; - default: - iErr = JX9_CTX_ERR; - break; - } - SyBlobAppend(pWorker, zErr, SyStrlen(zErr)); - if( pFuncName ){ - /* Append function name first */ - SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte); - SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1); - } - SyBlobAppend(pWorker, zMessage, SyStrlen(zMessage)); - /* Consume the error message */ - rc = VmCallErrorHandler(&(*pVm), pWorker); - return rc; -} -/* - * Format and throw a run-time error and invoke the supplied VM output consumer callback. - * Refer to the implementation of [jx9_context_throw_error_format()] for additional - * information. - */ -static sxi32 VmThrowErrorAp( - jx9_vm *pVm, /* Target VM */ - SyString *pFuncName, /* Function name. NULL otherwise */ - sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice] */ - const char *zFormat, /* Format message */ - va_list ap /* Variable list of arguments */ - ) -{ - SyBlob *pWorker = &pVm->sWorker; - SyString *pFile; - char *zErr; - sxi32 rc; - if( !pVm->bErrReport ){ - /* Don't bother reporting errors */ - return SXRET_OK; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - /* Peek the processed file if available */ - pFile = (SyString *)SySetPeek(&pVm->aFiles); - if( pFile ){ - /* Append file name */ - SyBlobAppend(pWorker, pFile->zString, pFile->nByte); - SyBlobAppend(pWorker, (const void *)" ", sizeof(char)); - } - zErr = "Error: "; - switch(iErr){ - case JX9_CTX_WARNING: zErr = "Warning: "; break; - case JX9_CTX_NOTICE: zErr = "Notice: "; break; - default: - iErr = JX9_CTX_ERR; - break; - } - SyBlobAppend(pWorker, zErr, SyStrlen(zErr)); - if( pFuncName ){ - /* Append function name first */ - SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte); - SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1); - } - SyBlobFormatAp(pWorker, zFormat, ap); - /* Consume the error message */ - rc = VmCallErrorHandler(&(*pVm), pWorker); - return rc; -} -/* - * Format and throw a run-time error and invoke the supplied VM output consumer callback. - * Refer to the implementation of [jx9_context_throw_error_format()] for additional - * information. - * ------------------------------------ - * Simple boring wrapper function. - * ------------------------------------ - */ -static sxi32 VmErrorFormat(jx9_vm *pVm, sxi32 iErr, const char *zFormat, ...) -{ - va_list ap; - sxi32 rc; - va_start(ap, zFormat); - rc = VmThrowErrorAp(&(*pVm), 0, iErr, zFormat, ap); - va_end(ap); - return rc; -} -/* - * Format and throw a run-time error and invoke the supplied VM output consumer callback. - * Refer to the implementation of [jx9_context_throw_error_format()] for additional - * information. - * ------------------------------------ - * Simple boring wrapper function. - * ------------------------------------ - */ -JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap) -{ - sxi32 rc; - rc = VmThrowErrorAp(&(*pVm), &(*pFuncName), iErr, zFormat, ap); - return rc; -} -/* Forward declaration */ -static sxi32 VmLocalExec(jx9_vm *pVm,SySet *pByteCode,jx9_value *pResult); -/* - * Execute as much of a JX9 bytecode program as we can then return. - * - * [jx9VmMakeReady()] must be called before this routine in order to - * close the program with a final OP_DONE and to set up the default - * consumer routines and other stuff. Refer to the implementation - * of [jx9VmMakeReady()] for additional information. - * If the installed VM output consumer callback ever returns JX9_ABORT - * then the program execution is halted. - * After this routine has finished, [jx9VmRelease()] or [jx9VmReset()] - * should be used respectively to clean up the mess that was left behind - * or to reset the VM to it's initial state. - */ -static sxi32 VmByteCodeExec( - jx9_vm *pVm, /* Target VM */ - VmInstr *aInstr, /* JX9 bytecode program */ - jx9_value *pStack, /* Operand stack */ - int nTos, /* Top entry in the operand stack (usually -1) */ - jx9_value *pResult /* Store program return value here. NULL otherwise */ - ) -{ - VmInstr *pInstr; - jx9_value *pTos; - SySet aArg; - sxi32 pc; - sxi32 rc; - /* Argument container */ - SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *)); - if( nTos < 0 ){ - pTos = &pStack[-1]; - }else{ - pTos = &pStack[nTos]; - } - pc = 0; - /* Execute as much as we can */ - for(;;){ - /* Fetch the instruction to execute */ - pInstr = &aInstr[pc]; - rc = SXRET_OK; -/* - * What follows here is a massive switch statement where each case implements a - * separate instruction in the virtual machine. If we follow the usual - * indentation convention each case should be indented by 6 spaces. But - * that is a lot of wasted space on the left margin. So the code within - * the switch statement will break with convention and be flush-left. - */ - switch(pInstr->iOp){ -/* - * DONE: P1 * * - * - * Program execution completed: Clean up the mess left behind - * and return immediately. - */ -case JX9_OP_DONE: - if( pInstr->iP1 ){ -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( pResult ){ - /* Execution result */ - jx9MemObjStore(pTos, pResult); - } - VmPopOperand(&pTos, 1); - } - goto Done; -/* - * HALT: P1 * * - * - * Program execution aborted: Clean up the mess left behind - * and abort immediately. - */ -case JX9_OP_HALT: - if( pInstr->iP1 ){ -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( pTos->iFlags & MEMOBJ_STRING ){ - if( SyBlobLength(&pTos->sBlob) > 0 ){ - /* Output the exit message */ - pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob), - pVm->sVmConsumer.pUserData); - /* Increment output length */ - pVm->nOutputLen += SyBlobLength(&pTos->sBlob); - } - }else if(pTos->iFlags & MEMOBJ_INT ){ - /* Record exit status */ - pVm->iExitStatus = (sxi32)pTos->x.iVal; - } - VmPopOperand(&pTos, 1); - } - goto Abort; -/* - * JMP: * P2 * - * - * Unconditional jump: The next instruction executed will be - * the one at index P2 from the beginning of the program. - */ -case JX9_OP_JMP: - pc = pInstr->iP2 - 1; - break; -/* - * JZ: P1 P2 * - * - * Take the jump if the top value is zero (FALSE jump).Pop the top most - * entry in the stack if P1 is zero. - */ -case JX9_OP_JZ: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Get a boolean value */ - if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - if( !pTos->x.iVal ){ - /* Take the jump */ - pc = pInstr->iP2 - 1; - } - if( !pInstr->iP1 ){ - VmPopOperand(&pTos, 1); - } - break; -/* - * JNZ: P1 P2 * - * - * Take the jump if the top value is not zero (TRUE jump).Pop the top most - * entry in the stack if P1 is zero. - */ -case JX9_OP_JNZ: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Get a boolean value */ - if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - if( pTos->x.iVal ){ - /* Take the jump */ - pc = pInstr->iP2 - 1; - } - if( !pInstr->iP1 ){ - VmPopOperand(&pTos, 1); - } - break; -/* - * NOOP: * * * - * - * Do nothing. This instruction is often useful as a jump - * destination. - */ -case JX9_OP_NOOP: - break; -/* - * POP: P1 * * - * - * Pop P1 elements from the operand stack. - */ -case JX9_OP_POP: { - sxi32 n = pInstr->iP1; - if( &pTos[-n+1] < pStack ){ - /* TICKET 1433-51 Stack underflow must be handled at run-time */ - n = (sxi32)(pTos - pStack); - } - VmPopOperand(&pTos, n); - break; - } -/* - * CVT_INT: * * * - * - * Force the top of the stack to be an integer. - */ -case JX9_OP_CVT_INT: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if((pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_INT); - break; -/* - * CVT_REAL: * * * - * - * Force the top of the stack to be a real. - */ -case JX9_OP_CVT_REAL: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if((pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_REAL); - break; -/* - * CVT_STR: * * * - * - * Force the top of the stack to be a string. - */ -case JX9_OP_CVT_STR: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pTos); - } - break; -/* - * CVT_BOOL: * * * - * - * Force the top of the stack to be a boolean. - */ -case JX9_OP_CVT_BOOL: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - break; -/* - * CVT_NULL: * * * - * - * Nullify the top of the stack. - */ -case JX9_OP_CVT_NULL: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - jx9MemObjRelease(pTos); - break; -/* - * CVT_NUMC: * * * - * - * Force the top of the stack to be a numeric type (integer, real or both). - */ -case JX9_OP_CVT_NUMC: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a numeric cast */ - jx9MemObjToNumeric(pTos); - break; -/* - * CVT_ARRAY: * * * - * - * Force the top of the stack to be a hashmap aka 'array'. - */ -case JX9_OP_CVT_ARRAY: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a hashmap cast */ - rc = jx9MemObjToHashmap(pTos); - if( rc != SXRET_OK ){ - /* Not so fatal, emit a simple warning */ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING, - "JX9 engine is running out of memory while performing an array cast"); - } - break; -/* - * LOADC P1 P2 * - * - * Load a constant [i.e: JX9_EOL, JX9_OS, __TIME__, ...] indexed at P2 in the constant pool. - * If P1 is set, then this constant is candidate for expansion via user installable callbacks. - */ -case JX9_OP_LOADC: { - jx9_value *pObj; - /* Reserve a room */ - pTos++; - if( (pObj = (jx9_value *)SySetAt(&pVm->aLitObj, pInstr->iP2)) != 0 ){ - if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){ - SyHashEntry *pEntry; - /* Candidate for expansion via user defined callbacks */ - pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - if( pEntry ){ - jx9_constant *pCons = (jx9_constant *)pEntry->pUserData; - /* Set a NULL default value */ - MemObjSetType(pTos, MEMOBJ_NULL); - SyBlobReset(&pTos->sBlob); - /* Invoke the callback and deal with the expanded value */ - pCons->xExpand(pTos, pCons->pUserData); - /* Mark as constant */ - pTos->nIdx = SXU32_HIGH; - break; - } - } - jx9MemObjLoad(pObj, pTos); - }else{ - /* Set a NULL value */ - MemObjSetType(pTos, MEMOBJ_NULL); - } - /* Mark as constant */ - pTos->nIdx = SXU32_HIGH; - break; - } -/* - * LOAD: P1 * P3 - * - * Load a variable where it's name is taken from the top of the stack or - * from the P3 operand. - * If P1 is set, then perform a lookup only.In other words do not create - * the variable if non existent and push the NULL constant instead. - */ -case JX9_OP_LOAD:{ - jx9_value *pObj; - SyString sName; - if( pInstr->p3 == 0 ){ - /* Take the variable name from the top of the stack */ -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a string cast */ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pTos); - } - SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - }else{ - SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); - /* Reserve a room for the target object */ - pTos++; - } - /* Extract the requested memory object */ - pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, pInstr->iP1 != 1); - if( pObj == 0 ){ - if( pInstr->iP1 ){ - /* Variable not found, load NULL */ - if( !pInstr->p3 ){ - jx9MemObjRelease(pTos); - }else{ - MemObjSetType(pTos, MEMOBJ_NULL); - } - pTos->nIdx = SXU32_HIGH; /* Mark as constant */ - break; - }else{ - /* Fatal error */ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName); - goto Abort; - } - } - /* Load variable contents */ - jx9MemObjLoad(pObj, pTos); - pTos->nIdx = pObj->nIdx; - break; - } -/* - * LOAD_MAP P1 * * - * - * Allocate a new empty hashmap (array in the JX9 jargon) and push it on the stack. - * If the P1 operand is greater than zero then pop P1 elements from the - * stack and insert them (key => value pair) in the new hashmap. - */ -case JX9_OP_LOAD_MAP: { - jx9_hashmap *pMap; - int is_json_object; /* TRUE if we are dealing with a JSON object */ - int iIncr = 1; - /* Allocate a new hashmap instance */ - pMap = jx9NewHashmap(&(*pVm), 0, 0); - if( pMap == 0 ){ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, - "Fatal, JX9 engine is running out of memory while loading JSON array/object at instruction #:%d", pc); - goto Abort; - } - is_json_object = 0; - if( pInstr->iP2 ){ - /* JSON object, record that */ - pMap->iFlags |= HASHMAP_JSON_OBJECT; - is_json_object = 1; - iIncr = 2; - } - if( pInstr->iP1 > 0 ){ - jx9_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */ - /* Perform the insertion */ - while( pEntry <= pTos ){ - /* Standard insertion */ - jx9HashmapInsert(pMap, - is_json_object ? pEntry : 0 /* Automatic index assign */, - is_json_object ? &pEntry[1] : pEntry - ); - /* Next pair on the stack */ - pEntry += iIncr; - } - /* Pop P1 elements */ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Push the hashmap */ - pTos++; - pTos->x.pOther = pMap; - MemObjSetType(pTos, MEMOBJ_HASHMAP); - break; - } -/* - * LOAD_IDX: P1 P2 * - * - * Load a hasmap entry where it's index (either numeric or string) is taken - * from the stack. - * If the index does not refer to a valid element, then push the NULL constant - * instead. - */ -case JX9_OP_LOAD_IDX: { - jx9_hashmap_node *pNode = 0; /* cc warning */ - jx9_hashmap *pMap = 0; - jx9_value *pIdx; - pIdx = 0; - if( pInstr->iP1 == 0 ){ - if( !pInstr->iP2){ - /* No available index, load NULL */ - if( pTos >= pStack ){ - jx9MemObjRelease(pTos); - }else{ - /* TICKET 1433-020: Empty stack */ - pTos++; - MemObjSetType(pTos, MEMOBJ_NULL); - pTos->nIdx = SXU32_HIGH; - } - /* Emit a notice */ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_NOTICE, - "JSON Array/Object: Attempt to access an undefined member, JX9 is loading NULL"); - break; - } - }else{ - pIdx = pTos; - pTos--; - } - if( pTos->iFlags & MEMOBJ_STRING ){ - /* String access */ - if( pIdx ){ - sxu32 nOfft; - if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){ - /* Force an int cast */ - jx9MemObjToInteger(pIdx); - } - nOfft = (sxu32)pIdx->x.iVal; - if( nOfft >= SyBlobLength(&pTos->sBlob) ){ - /* Invalid offset, load null */ - jx9MemObjRelease(pTos); - }else{ - const char *zData = (const char *)SyBlobData(&pTos->sBlob); - int c = zData[nOfft]; - jx9MemObjRelease(pTos); - MemObjSetType(pTos, MEMOBJ_STRING); - SyBlobAppend(&pTos->sBlob, (const void *)&c, sizeof(char)); - } - }else{ - /* No available index, load NULL */ - MemObjSetType(pTos, MEMOBJ_NULL); - } - break; - } - if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){ - if( pTos->nIdx != SXU32_HIGH ){ - jx9_value *pObj; - if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjToHashmap(pObj); - jx9MemObjLoad(pObj, pTos); - } - } - } - rc = SXERR_NOTFOUND; /* Assume the index is invalid */ - if( pTos->iFlags & MEMOBJ_HASHMAP ){ - /* Point to the hashmap */ - pMap = (jx9_hashmap *)pTos->x.pOther; - if( pIdx ){ - /* Load the desired entry */ - rc = jx9HashmapLookup(pMap, pIdx, &pNode); - } - if( rc != SXRET_OK && pInstr->iP2 ){ - /* Create a new empty entry */ - rc = jx9HashmapInsert(pMap, pIdx, 0); - if( rc == SXRET_OK ){ - /* Point to the last inserted entry */ - pNode = pMap->pLast; - } - } - } - if( pIdx ){ - jx9MemObjRelease(pIdx); - } - if( rc == SXRET_OK ){ - /* Load entry contents */ - if( pMap->iRef < 2 ){ - /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy - * of the entry value, rather than pointing to it. - */ - pTos->nIdx = SXU32_HIGH; - jx9HashmapExtractNodeValue(pNode, pTos, TRUE); - }else{ - pTos->nIdx = pNode->nValIdx; - jx9HashmapExtractNodeValue(pNode, pTos, FALSE); - jx9HashmapUnref(pMap); - } - }else{ - /* No such entry, load NULL */ - jx9MemObjRelease(pTos); - pTos->nIdx = SXU32_HIGH; - } - break; - } -/* - * STORE * P2 P3 - * - * Perform a store (Assignment) operation. - */ -case JX9_OP_STORE: { - jx9_value *pObj; - SyString sName; -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( pInstr->iP2 ){ - sxu32 nIdx; - /* Member store operation */ - nIdx = pTos->nIdx; - VmPopOperand(&pTos, 1); - if( nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, - "Cannot perform assignment on a constant object attribute, JX9 is loading NULL"); - pTos->nIdx = SXU32_HIGH; - }else{ - /* Point to the desired memory object */ - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - if( pObj ){ - /* Perform the store operation */ - jx9MemObjStore(pTos, pObj); - } - } - break; - }else if( pInstr->p3 == 0 ){ - /* Take the variable name from the next on the stack */ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - pTos--; -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - }else{ - SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); - } - /* Extract the desired variable and if not available dynamically create it */ - pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, TRUE); - if( pObj == 0 ){ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, - "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName); - goto Abort; - } - if( !pInstr->p3 ){ - jx9MemObjRelease(&pTos[1]); - } - /* Perform the store operation */ - jx9MemObjStore(pTos, pObj); - break; - } -/* - * STORE_IDX: P1 * P3 - * - * Perfrom a store operation an a hashmap entry. - */ -case JX9_OP_STORE_IDX: { - jx9_hashmap *pMap = 0; /* cc warning */ - jx9_value *pKey; - sxu32 nIdx; - if( pInstr->iP1 ){ - /* Key is next on stack */ - pKey = pTos; - pTos--; - }else{ - pKey = 0; - } - nIdx = pTos->nIdx; - if( pTos->iFlags & MEMOBJ_HASHMAP ){ - /* Hashmap already loaded */ - pMap = (jx9_hashmap *)pTos->x.pOther; - if( pMap->iRef < 2 ){ - /* TICKET 1433-48: Prevent garbage collection */ - pMap->iRef = 2; - } - }else{ - jx9_value *pObj; - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - if( pObj == 0 ){ - if( pKey ){ - jx9MemObjRelease(pKey); - } - VmPopOperand(&pTos, 1); - break; - } - /* Phase#1: Load the array */ - if( (pObj->iFlags & MEMOBJ_STRING) ){ - VmPopOperand(&pTos, 1); - if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - if( pKey == 0 ){ - /* Append string */ - if( SyBlobLength(&pTos->sBlob) > 0 ){ - SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - } - }else{ - sxu32 nOfft; - if((pKey->iFlags & MEMOBJ_INT)){ - /* Force an int cast */ - jx9MemObjToInteger(pKey); - } - nOfft = (sxu32)pKey->x.iVal; - if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){ - const char *zBlob = (const char *)SyBlobData(&pTos->sBlob); - char *zData = (char *)SyBlobData(&pObj->sBlob); - zData[nOfft] = zBlob[0]; - }else{ - if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){ - /* Perform an append operation */ - SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), sizeof(char)); - } - } - } - if( pKey ){ - jx9MemObjRelease(pKey); - } - break; - }else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* Force a hashmap cast */ - rc = jx9MemObjToHashmap(pObj); - if( rc != SXRET_OK ){ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while creating a new array"); - goto Abort; - } - } - pMap = (jx9_hashmap *)pObj->x.pOther; - } - VmPopOperand(&pTos, 1); - /* Phase#2: Perform the insertion */ - jx9HashmapInsert(pMap, pKey, pTos); - if( pKey ){ - jx9MemObjRelease(pKey); - } - break; - } -/* - * INCR: P1 * * - * - * Force a numeric cast and increment the top of the stack by 1. - * If the P1 operand is set then perform a duplication of the top of - * the stack and increment after that. - */ -case JX9_OP_INCR: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){ - if( pTos->nIdx != SXU32_HIGH ){ - jx9_value *pObj; - if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - /* Force a numeric cast */ - jx9MemObjToNumeric(pObj); - if( pObj->iFlags & MEMOBJ_REAL ){ - pObj->x.rVal++; - /* Try to get an integer representation */ - jx9MemObjTryInteger(pTos); - }else{ - pObj->x.iVal++; - MemObjSetType(pTos, MEMOBJ_INT); - } - if( pInstr->iP1 ){ - /* Pre-icrement */ - jx9MemObjStore(pObj, pTos); - } - } - }else{ - if( pInstr->iP1 ){ - /* Force a numeric cast */ - jx9MemObjToNumeric(pTos); - /* Pre-increment */ - if( pTos->iFlags & MEMOBJ_REAL ){ - pTos->x.rVal++; - /* Try to get an integer representation */ - jx9MemObjTryInteger(pTos); - }else{ - pTos->x.iVal++; - MemObjSetType(pTos, MEMOBJ_INT); - } - } - } - } - break; -/* - * DECR: P1 * * - * - * Force a numeric cast and decrement the top of the stack by 1. - * If the P1 operand is set then perform a duplication of the top of the stack - * and decrement after that. - */ -case JX9_OP_DECR: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){ - /* Force a numeric cast */ - jx9MemObjToNumeric(pTos); - if( pTos->nIdx != SXU32_HIGH ){ - jx9_value *pObj; - if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - /* Force a numeric cast */ - jx9MemObjToNumeric(pObj); - if( pObj->iFlags & MEMOBJ_REAL ){ - pObj->x.rVal--; - /* Try to get an integer representation */ - jx9MemObjTryInteger(pTos); - }else{ - pObj->x.iVal--; - MemObjSetType(pTos, MEMOBJ_INT); - } - if( pInstr->iP1 ){ - /* Pre-icrement */ - jx9MemObjStore(pObj, pTos); - } - } - }else{ - if( pInstr->iP1 ){ - /* Pre-increment */ - if( pTos->iFlags & MEMOBJ_REAL ){ - pTos->x.rVal--; - /* Try to get an integer representation */ - jx9MemObjTryInteger(pTos); - }else{ - pTos->x.iVal--; - MemObjSetType(pTos, MEMOBJ_INT); - } - } - } - } - break; -/* - * UMINUS: * * * - * - * Perform a unary minus operation. - */ -case JX9_OP_UMINUS: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a numeric (integer, real or both) cast */ - jx9MemObjToNumeric(pTos); - if( pTos->iFlags & MEMOBJ_REAL ){ - pTos->x.rVal = -pTos->x.rVal; - } - if( pTos->iFlags & MEMOBJ_INT ){ - pTos->x.iVal = -pTos->x.iVal; - } - break; -/* - * UPLUS: * * * - * - * Perform a unary plus operation. - */ -case JX9_OP_UPLUS: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a numeric (integer, real or both) cast */ - jx9MemObjToNumeric(pTos); - if( pTos->iFlags & MEMOBJ_REAL ){ - pTos->x.rVal = +pTos->x.rVal; - } - if( pTos->iFlags & MEMOBJ_INT ){ - pTos->x.iVal = +pTos->x.iVal; - } - break; -/* - * OP_LNOT: * * * - * - * Interpret the top of the stack as a boolean value. Replace it - * with its complement. - */ -case JX9_OP_LNOT: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a boolean cast */ - if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - pTos->x.iVal = !pTos->x.iVal; - break; -/* - * OP_BITNOT: * * * - * - * Interpret the top of the stack as an value.Replace it - * with its ones-complement. - */ -case JX9_OP_BITNOT: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force an integer cast */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - pTos->x.iVal = ~pTos->x.iVal; - break; -/* OP_MUL * * * - * OP_MUL_STORE * * * - * - * Pop the top two elements from the stack, multiply them together, - * and push the result back onto the stack. - */ -case JX9_OP_MUL: -case JX9_OP_MUL_STORE: { - jx9_value *pNos = &pTos[-1]; - /* Force the operand to be numeric */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - jx9MemObjToNumeric(pTos); - jx9MemObjToNumeric(pNos); - /* Perform the requested operation */ - if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ - /* Floating point arithemic */ - jx9_real a, b, r; - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - a = pNos->x.rVal; - b = pTos->x.rVal; - r = a * b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - }else{ - /* Integer arithmetic */ - sxi64 a, b, r; - a = pNos->x.iVal; - b = pTos->x.iVal; - r = a * b; - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - } - if( pInstr->iOp == JX9_OP_MUL_STORE ){ - jx9_value *pObj; - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - } - VmPopOperand(&pTos, 1); - break; - } -/* OP_ADD * * * - * - * Pop the top two elements from the stack, add them together, - * and push the result back onto the stack. - */ -case JX9_OP_ADD:{ - jx9_value *pNos = &pTos[-1]; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Perform the addition */ - jx9MemObjAdd(pNos, pTos, FALSE); - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_ADD_STORE * * * - * - * Pop the top two elements from the stack, add them together, - * and push the result back onto the stack. - */ -case JX9_OP_ADD_STORE:{ - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - sxu32 nIdx; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Perform the addition */ - nIdx = pTos->nIdx; - jx9MemObjAdd(pTos, pNos, TRUE); - /* Peform the store operation */ - if( nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx)) != 0 ){ - jx9MemObjStore(pTos, pObj); - } - /* Ticket 1433-35: Perform a stack dup */ - jx9MemObjStore(pTos, pNos); - VmPopOperand(&pTos, 1); - break; - } -/* OP_SUB * * * - * - * Pop the top two elements from the stack, subtract the - * first (what was next on the stack) from the second (the - * top of the stack) and push the result back onto the stack. - */ -case JX9_OP_SUB: { - jx9_value *pNos = &pTos[-1]; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ - /* Floating point arithemic */ - jx9_real a, b, r; - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - a = pNos->x.rVal; - b = pTos->x.rVal; - r = a - b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - }else{ - /* Integer arithmetic */ - sxi64 a, b, r; - a = pNos->x.iVal; - b = pTos->x.iVal; - r = a - b; - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - } - VmPopOperand(&pTos, 1); - break; - } -/* OP_SUB_STORE * * * - * - * Pop the top two elements from the stack, subtract the - * first (what was next on the stack) from the second (the - * top of the stack) and push the result back onto the stack. - */ -case JX9_OP_SUB_STORE: { - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ - /* Floating point arithemic */ - jx9_real a, b, r; - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - a = pTos->x.rVal; - b = pNos->x.rVal; - r = a - b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - }else{ - /* Integer arithmetic */ - sxi64 a, b, r; - a = pTos->x.iVal; - b = pNos->x.iVal; - r = a - b; - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - } - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } - -/* - * OP_MOD * * * - * - * Pop the top two elements from the stack, divide the - * first (what was next on the stack) from the second (the - * top of the stack) and push the remainder after division - * onto the stack. - * Note: Only integer arithemtic is allowed. - */ -case JX9_OP_MOD:{ - jx9_value *pNos = &pTos[-1]; - sxi64 a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pNos->x.iVal; - b = pTos->x.iVal; - if( b == 0 ){ - r = 0; - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a); - /* goto Abort; */ - }else{ - r = a%b; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_MOD_STORE * * * - * - * Pop the top two elements from the stack, divide the - * first (what was next on the stack) from the second (the - * top of the stack) and push the remainder after division - * onto the stack. - * Note: Only integer arithemtic is allowed. - */ -case JX9_OP_MOD_STORE: { - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - sxi64 a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pTos->x.iVal; - b = pNos->x.iVal; - if( b == 0 ){ - r = 0; - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a); - /* goto Abort; */ - }else{ - r = a%b; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_DIV * * * - * - * Pop the top two elements from the stack, divide the - * first (what was next on the stack) from the second (the - * top of the stack) and push the result onto the stack. - * Note: Only floating point arithemtic is allowed. - */ -case JX9_OP_DIV:{ - jx9_value *pNos = &pTos[-1]; - jx9_real a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be real */ - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - /* Perform the requested operation */ - a = pNos->x.rVal; - b = pTos->x.rVal; - if( b == 0 ){ - /* Division by zero */ - r = 0; - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Division by zero"); - /* goto Abort; */ - }else{ - r = a/b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - } - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_DIV_STORE * * * - * - * Pop the top two elements from the stack, divide the - * first (what was next on the stack) from the second (the - * top of the stack) and push the result onto the stack. - * Note: Only floating point arithemtic is allowed. - */ -case JX9_OP_DIV_STORE:{ - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - jx9_real a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be real */ - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - /* Perform the requested operation */ - a = pTos->x.rVal; - b = pNos->x.rVal; - if( b == 0 ){ - /* Division by zero */ - r = 0; - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd/0", a); - /* goto Abort; */ - }else{ - r = a/b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - } - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } -/* OP_BAND * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise AND of the - * two elements. -*/ -/* OP_BOR * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise OR of the - * two elements. - */ -/* OP_BXOR * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise XOR of the - * two elements. - */ -case JX9_OP_BAND: -case JX9_OP_BOR: -case JX9_OP_BXOR:{ - jx9_value *pNos = &pTos[-1]; - sxi64 a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pNos->x.iVal; - b = pTos->x.iVal; - switch(pInstr->iOp){ - case JX9_OP_BOR_STORE: - case JX9_OP_BOR: r = a|b; break; - case JX9_OP_BXOR_STORE: - case JX9_OP_BXOR: r = a^b; break; - case JX9_OP_BAND_STORE: - case JX9_OP_BAND: - default: r = a&b; break; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - VmPopOperand(&pTos, 1); - break; - } -/* OP_BAND_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise AND of the - * two elements. -*/ -/* OP_BOR_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise OR of the - * two elements. - */ -/* OP_BXOR_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise XOR of the - * two elements. - */ -case JX9_OP_BAND_STORE: -case JX9_OP_BOR_STORE: -case JX9_OP_BXOR_STORE:{ - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - sxi64 a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pTos->x.iVal; - b = pNos->x.iVal; - switch(pInstr->iOp){ - case JX9_OP_BOR_STORE: - case JX9_OP_BOR: r = a|b; break; - case JX9_OP_BXOR_STORE: - case JX9_OP_BXOR: r = a^b; break; - case JX9_OP_BAND_STORE: - case JX9_OP_BAND: - default: r = a&b; break; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } -/* OP_SHL * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the second element shifted - * left by N bits where N is the top element on the stack. - * Note: Only integer arithmetic is allowed. - */ -/* OP_SHR * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the second element shifted - * right by N bits where N is the top element on the stack. - * Note: Only integer arithmetic is allowed. - */ -case JX9_OP_SHL: -case JX9_OP_SHR: { - jx9_value *pNos = &pTos[-1]; - sxi64 a, r; - sxi32 b; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pNos->x.iVal; - b = (sxi32)pTos->x.iVal; - if( pInstr->iOp == JX9_OP_SHL ){ - r = a << b; - }else{ - r = a >> b; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - VmPopOperand(&pTos, 1); - break; - } -/* OP_SHL_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the second element shifted - * left by N bits where N is the top element on the stack. - * Note: Only integer arithmetic is allowed. - */ -/* OP_SHR_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the second element shifted - * right by N bits where N is the top element on the stack. - * Note: Only integer arithmetic is allowed. - */ -case JX9_OP_SHL_STORE: -case JX9_OP_SHR_STORE: { - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - sxi64 a, r; - sxi32 b; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pTos->x.iVal; - b = (sxi32)pNos->x.iVal; - if( pInstr->iOp == JX9_OP_SHL_STORE ){ - r = a << b; - }else{ - r = a >> b; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } -/* CAT: P1 * * - * - * Pop P1 elements from the stack. Concatenate them togeher and push the result - * back. - */ -case JX9_OP_CAT:{ - jx9_value *pNos, *pCur; - if( pInstr->iP1 < 1 ){ - pNos = &pTos[-1]; - }else{ - pNos = &pTos[-pInstr->iP1+1]; - } -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force a string cast */ - if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pNos); - } - pCur = &pNos[1]; - while( pCur <= pTos ){ - if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pCur); - } - /* Perform the concatenation */ - if( SyBlobLength(&pCur->sBlob) > 0 ){ - jx9MemObjStringAppend(pNos, (const char *)SyBlobData(&pCur->sBlob), SyBlobLength(&pCur->sBlob)); - } - SyBlobRelease(&pCur->sBlob); - pCur++; - } - pTos = pNos; - break; - } -/* CAT_STORE: * * * - * - * Pop two elements from the stack. Concatenate them togeher and push the result - * back. - */ -case JX9_OP_CAT_STORE:{ - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - if((pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - if((pNos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pNos); - } - /* Perform the concatenation (Reverse order) */ - if( SyBlobLength(&pNos->sBlob) > 0 ){ - jx9MemObjStringAppend(pTos, (const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob)); - } - /* Perform the store operation */ - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pTos, pObj); - } - jx9MemObjStore(pTos, pNos); - VmPopOperand(&pTos, 1); - break; - } -/* OP_AND: * * * - * - * Pop two values off the stack. Take the logical AND of the - * two values and push the resulting boolean value back onto the - * stack. - */ -/* OP_OR: * * * - * - * Pop two values off the stack. Take the logical OR of the - * two values and push the resulting boolean value back onto the - * stack. - */ -case JX9_OP_LAND: -case JX9_OP_LOR: { - jx9_value *pNos = &pTos[-1]; - sxi32 v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force a boolean cast */ - if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pNos); - } - v1 = pNos->x.iVal == 0 ? 1 : 0; - v2 = pTos->x.iVal == 0 ? 1 : 0; - if( pInstr->iOp == JX9_OP_LAND ){ - static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; - v1 = and_logic[v1*3+v2]; - }else{ - static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; - v1 = or_logic[v1*3+v2]; - } - if( v1 == 2 ){ - v1 = 1; - } - VmPopOperand(&pTos, 1); - pTos->x.iVal = v1 == 0 ? 1 : 0; - MemObjSetType(pTos, MEMOBJ_BOOL); - break; - } -/* OP_LXOR: * * * - * - * Pop two values off the stack. Take the logical XOR of the - * two values and push the resulting boolean value back onto the - * stack. - * According to the JX9 language reference manual: - * $a xor $b is evaluated to TRUE if either $a or $b is - * TRUE, but not both. - */ -case JX9_OP_LXOR:{ - jx9_value *pNos = &pTos[-1]; - sxi32 v = 0; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force a boolean cast */ - if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pNos); - } - if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){ - v = 1; - } - VmPopOperand(&pTos, 1); - pTos->x.iVal = v; - MemObjSetType(pTos, MEMOBJ_BOOL); - break; - } -/* OP_EQ P1 P2 P3 - * - * Pop the top two elements from the stack. If they are equal, then - * jump to instruction P2. Otherwise, continue to the next instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - */ -/* OP_NEQ P1 P2 P3 - * - * Pop the top two elements from the stack. If they are not equal, then - * jump to instruction P2. Otherwise, continue to the next instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - */ -case JX9_OP_EQ: -case JX9_OP_NEQ: { - jx9_value *pNos = &pTos[-1]; - /* Perform the comparison and act accordingly */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - rc = jx9MemObjCmp(pNos, pTos, FALSE, 0); - if( pInstr->iOp == JX9_OP_EQ ){ - rc = rc == 0; - }else{ - rc = rc != 0; - } - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* OP_TEQ P1 P2 * - * - * Pop the top two elements from the stack. If they have the same type and are equal - * then jump to instruction P2. Otherwise, continue to the next instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - */ -case JX9_OP_TEQ: { - jx9_value *pNos = &pTos[-1]; - /* Perform the comparison and act accordingly */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) == 0; - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* OP_TNE P1 P2 * - * - * Pop the top two elements from the stack.If they are not equal an they are not - * of the same type, then jump to instruction P2. Otherwise, continue to the next - * instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - * - */ -case JX9_OP_TNE: { - jx9_value *pNos = &pTos[-1]; - /* Perform the comparison and act accordingly */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) != 0; - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* OP_LT P1 P2 P3 - * - * Pop the top two elements from the stack. If the second element (the top of stack) - * is less than the first (next on stack), then jump to instruction P2.Otherwise - * continue to the next instruction. In other words, jump if pNosiOp == JX9_OP_LE ){ - rc = rc < 1; - }else{ - rc = rc < 0; - } - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* OP_GT P1 P2 P3 - * - * Pop the top two elements from the stack. If the second element (the top of stack) - * is greater than the first (next on stack), then jump to instruction P2.Otherwise - * continue to the next instruction. In other words, jump if pNosiOp == JX9_OP_GE ){ - rc = rc >= 0; - }else{ - rc = rc > 0; - } - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* - * OP_FOREACH_INIT * P2 P3 - * Prepare a foreach step. - */ -case JX9_OP_FOREACH_INIT: { - jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3; - void *pName; -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( SyStringLength(&pInfo->sValue) < 1 ){ - /* Take the variable name from the top of the stack */ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - /* Duplicate name */ - if( SyBlobLength(&pTos->sBlob) > 0 ){ - pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - SyStringInitFromBuf(&pInfo->sValue, pName, SyBlobLength(&pTos->sBlob)); - } - VmPopOperand(&pTos, 1); - } - if( (pInfo->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - /* Duplicate name */ - if( SyBlobLength(&pTos->sBlob) > 0 ){ - pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - SyStringInitFromBuf(&pInfo->sKey, pName, SyBlobLength(&pTos->sBlob)); - } - VmPopOperand(&pTos, 1); - } - /* Make sure we are dealing with a hashmap [i.e. JSON array or object ]*/ - if( (pTos->iFlags & (MEMOBJ_HASHMAP)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){ - /* Jump out of the loop */ - if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING, - "Invalid argument supplied for the foreach statement, expecting JSON array or object instance"); - } - pc = pInstr->iP2 - 1; - }else{ - jx9_foreach_step *pStep; - pStep = (jx9_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_foreach_step)); - if( pStep == 0 ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step"); - /* Jump out of the loop */ - pc = pInstr->iP2 - 1; - }else{ - /* Zero the structure */ - SyZero(pStep, sizeof(jx9_foreach_step)); - /* Prepare the step */ - pStep->iFlags = pInfo->iFlags; - if( pTos->iFlags & MEMOBJ_HASHMAP ){ - jx9_hashmap *pMap = (jx9_hashmap *)pTos->x.pOther; - /* Reset the internal loop cursor */ - jx9HashmapResetLoopCursor(pMap); - /* Mark the step */ - pStep->pMap = pMap; - pMap->iRef++; - } - } - if( SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep) ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step"); - SyMemBackendPoolFree(&pVm->sAllocator, pStep); - /* Jump out of the loop */ - pc = pInstr->iP2 - 1; - } - } - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_FOREACH_STEP * P2 P3 - * Perform a foreach step. Jump to P2 at the end of the step. - */ -case JX9_OP_FOREACH_STEP: { - jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3; - jx9_foreach_step **apStep, *pStep; - jx9_hashmap_node *pNode; - jx9_hashmap *pMap; - jx9_value *pValue; - /* Peek the last step */ - apStep = (jx9_foreach_step **)SySetBasePtr(&pInfo->aStep); - pStep = apStep[SySetUsed(&pInfo->aStep) - 1]; - pMap = pStep->pMap; - /* Extract the current node value */ - pNode = jx9HashmapGetNextEntry(pMap); - if( pNode == 0 ){ - /* No more entry to process */ - pc = pInstr->iP2 - 1; /* Jump to this destination */ - /* Automatically reset the loop cursor */ - jx9HashmapResetLoopCursor(pMap); - /* Cleanup the mess left behind */ - SyMemBackendPoolFree(&pVm->sAllocator, pStep); - SySetPop(&pInfo->aStep); - jx9HashmapUnref(pMap); - }else{ - if( (pStep->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){ - jx9_value *pKey = VmExtractMemObj(&(*pVm), &pInfo->sKey, FALSE, TRUE); - if( pKey ){ - jx9HashmapExtractNodeKey(pNode, pKey); - } - } - /* Make a copy of the entry value */ - pValue = VmExtractMemObj(&(*pVm), &pInfo->sValue, FALSE, TRUE); - if( pValue ){ - jx9HashmapExtractNodeValue(pNode, pValue, TRUE); - } - } - break; - } -/* - * OP_MEMBER P1 P2 - * Load JSON object entry on the stack. - */ -case JX9_OP_MEMBER: { - jx9_hashmap_node *pNode = 0; /* cc warning */ - jx9_hashmap *pMap = 0; - jx9_value *pIdx; - pIdx = pTos; - pTos--; - rc = SXERR_NOTFOUND; /* Assume the index is invalid */ - if( pTos->iFlags & MEMOBJ_HASHMAP ){ - /* Point to the hashmap */ - pMap = (jx9_hashmap *)pTos->x.pOther; - /* Load the desired entry */ - rc = jx9HashmapLookup(pMap, pIdx, &pNode); - } - jx9MemObjRelease(pIdx); - if( rc == SXRET_OK ){ - /* Load entry contents */ - if( pMap->iRef < 2 ){ - /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy - * of the entry value, rather than pointing to it. - */ - pTos->nIdx = SXU32_HIGH; - jx9HashmapExtractNodeValue(pNode, pTos, TRUE); - }else{ - pTos->nIdx = pNode->nValIdx; - jx9HashmapExtractNodeValue(pNode, pTos, FALSE); - jx9HashmapUnref(pMap); - } - }else{ - /* No such entry, load NULL */ - jx9MemObjRelease(pTos); - pTos->nIdx = SXU32_HIGH; - } - break; - } -/* - * OP_SWITCH * * P3 - * This is the bytecode implementation of the complex switch() JX9 construct. - */ -case JX9_OP_SWITCH: { - jx9_switch *pSwitch = (jx9_switch *)pInstr->p3; - jx9_case_expr *aCase, *pCase; - jx9_value sValue, sCaseValue; - sxu32 n, nEntry; -#ifdef UNTRUST - if( pSwitch == 0 || pTos < pStack ){ - goto Abort; - } -#endif - /* Point to the case table */ - aCase = (jx9_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr); - nEntry = SySetUsed(&pSwitch->aCaseExpr); - /* Select the appropriate case block to execute */ - jx9MemObjInit(pVm, &sValue); - jx9MemObjInit(pVm, &sCaseValue); - for( n = 0 ; n < nEntry ; ++n ){ - pCase = &aCase[n]; - jx9MemObjLoad(pTos, &sValue); - /* Execute the case expression first */ - VmLocalExec(pVm,&pCase->aByteCode, &sCaseValue); - /* Compare the two expression */ - rc = jx9MemObjCmp(&sValue, &sCaseValue, FALSE, 0); - jx9MemObjRelease(&sValue); - jx9MemObjRelease(&sCaseValue); - if( rc == 0 ){ - /* Value match, jump to this block */ - pc = pCase->nStart - 1; - break; - } - } - VmPopOperand(&pTos, 1); - if( n >= nEntry ){ - /* No approprite case to execute, jump to the default case */ - if( pSwitch->nDefault > 0 ){ - pc = pSwitch->nDefault - 1; - }else{ - /* No default case, jump out of this switch */ - pc = pSwitch->nOut - 1; - } - } - break; - } -/* - * OP_UPLINK P1 * * - * Link a variable to the top active VM frame. - * This is used to implement the 'uplink' JX9 construct. - */ -case JX9_OP_UPLINK: { - if( pVm->pFrame->pParent ){ - jx9_value *pLink = &pTos[-pInstr->iP1+1]; - SyString sName; - /* Perform the link */ - while( pLink <= pTos ){ - if((pLink->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pLink); - } - SyStringInitFromBuf(&sName, SyBlobData(&pLink->sBlob), SyBlobLength(&pLink->sBlob)); - if( sName.nByte > 0 ){ - VmFrameLink(&(*pVm), &sName); - } - pLink++; - } - } - VmPopOperand(&pTos, pInstr->iP1); - break; - } -/* - * OP_CALL P1 * * - * Call a JX9 or a foreign function and push the return value of the called - * function on the stack. - */ -case JX9_OP_CALL: { - jx9_value *pArg = &pTos[-pInstr->iP1]; - SyHashEntry *pEntry; - SyString sName; - /* Extract function name */ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Raise exception: Invalid function name */ - VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Invalid function name, JX9 is returning NULL."); - /* Pop given arguments */ - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - jx9MemObjRelease(pTos); - break; - } - SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - /* Check for a compiled function first */ - pEntry = SyHashGet(&pVm->hFunction, (const void *)sName.zString, sName.nByte); - if( pEntry ){ - jx9_vm_func_arg *aFormalArg; - jx9_value *pFrameStack; - jx9_vm_func *pVmFunc; - VmFrame *pFrame; - jx9_value *pObj; - VmSlot sArg; - sxu32 n; - pVmFunc = (jx9_vm_func *)pEntry->pUserData; - /* Check The recursion limit */ - if( pVm->nRecursionDepth > pVm->nMaxDepth ){ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, - "Recursion limit reached while invoking user function '%z', JX9 will set a NULL return value", - &pVmFunc->sName); - /* Pop given arguments */ - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - jx9MemObjRelease(pTos); - break; - } - if( pVmFunc->pNextName ){ - /* Function is candidate for overloading, select the appropriate function to call */ - pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos-pArg)); - } - /* Extract the formal argument set */ - aFormalArg = (jx9_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs); - /* Create a new VM frame */ - rc = VmEnterFrame(&(*pVm),pVmFunc,&pFrame); - if( rc != SXRET_OK ){ - /* Raise exception: Out of memory */ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, - "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.", - &pVmFunc->sName); - /* Pop given arguments */ - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - jx9MemObjRelease(pTos); - break; - } - if( SySetUsed(&pVmFunc->aStatic) > 0 ){ - jx9_vm_func_static_var *pStatic, *aStatic; - /* Install static variables */ - aStatic = (jx9_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic); - for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){ - pStatic = &aStatic[n]; - if( pStatic->nIdx == SXU32_HIGH ){ - /* Initialize the static variables */ - pObj = VmReserveMemObj(&(*pVm), &pStatic->nIdx); - if( pObj ){ - /* Assume a NULL initialization value */ - jx9MemObjInit(&(*pVm), pObj); - if( SySetUsed(&pStatic->aByteCode) > 0 ){ - /* Evaluate initialization expression (Any complex expression) */ - VmLocalExec(&(*pVm), &pStatic->aByteCode, pObj); - } - pObj->nIdx = pStatic->nIdx; - }else{ - continue; - } - } - /* Install in the current frame */ - SyHashInsert(&pFrame->hVar, SyStringData(&pStatic->sName), SyStringLength(&pStatic->sName), - SX_INT_TO_PTR(pStatic->nIdx)); - } - } - /* Push arguments in the local frame */ - n = 0; - while( pArg < pTos ){ - if( n < SySetUsed(&pVmFunc->aArgs) ){ - if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){ - /* NULL values are redirected to default arguments */ - rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pArg); - if( rc == JX9_ABORT ){ - goto Abort; - } - } - /* Make sure the given arguments are of the correct type */ - if( aFormalArg[n].nType > 0 ){ - if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){ - ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType); - /* Cast to the desired type */ - if( xCast ){ - xCast(pArg); - } - } - } - /* Pass by value, make a copy of the given argument */ - pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE); - }else{ - char zName[32]; - SyString sName; - /* Set a dummy name */ - sName.nByte = SyBufferFormat(zName, sizeof(zName), "[%u]apArg", n); - sName.zString = zName; - /* Annonymous argument */ - pObj = VmExtractMemObj(&(*pVm), &sName, TRUE, TRUE); - } - if( pObj ){ - jx9MemObjStore(pArg, pObj); - /* Insert argument index */ - sArg.nIdx = pObj->nIdx; - sArg.pUserData = 0; - SySetPut(&pFrame->sArg, (const void *)&sArg); - } - jx9MemObjRelease(pArg); - pArg++; - ++n; - } - /* Process default values */ - while( n < SySetUsed(&pVmFunc->aArgs) ){ - if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){ - pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE); - if( pObj ){ - /* Evaluate the default value and extract it's result */ - rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pObj); - if( rc == JX9_ABORT ){ - goto Abort; - } - /* Insert argument index */ - sArg.nIdx = pObj->nIdx; - sArg.pUserData = 0; - SySetPut(&pFrame->sArg, (const void *)&sArg); - /* Make sure the default argument is of the correct type */ - if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){ - ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType); - /* Cast to the desired type */ - xCast(pObj); - } - } - } - ++n; - } - /* Pop arguments, function name from the operand stack and assume the function - * does not return anything. - */ - jx9MemObjRelease(pTos); - pTos = &pTos[-pInstr->iP1]; - /* Allocate a new operand stack and evaluate the function body */ - pFrameStack = VmNewOperandStack(&(*pVm), SySetUsed(&pVmFunc->aByteCode)); - if( pFrameStack == 0 ){ - /* Raise exception: Out of memory */ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.", - &pVmFunc->sName); - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - break; - } - /* Increment nesting level */ - pVm->nRecursionDepth++; - /* Execute function body */ - rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(&pVmFunc->aByteCode), pFrameStack, -1, pTos); - /* Decrement nesting level */ - pVm->nRecursionDepth--; - /* Free the operand stack */ - SyMemBackendFree(&pVm->sAllocator, pFrameStack); - /* Leave the frame */ - VmLeaveFrame(&(*pVm)); - if( rc == JX9_ABORT ){ - /* Abort processing immeditaley */ - goto Abort; - } - }else{ - jx9_user_func *pFunc; - jx9_context sCtx; - jx9_value sRet; - /* Look for an installed foreign function */ - pEntry = SyHashGet(&pVm->hHostFunction, (const void *)sName.zString, sName.nByte); - if( pEntry == 0 ){ - /* Call to undefined function */ - VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Call to undefined function '%z', JX9 is returning NULL.", &sName); - /* Pop given arguments */ - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - jx9MemObjRelease(pTos); - break; - } - pFunc = (jx9_user_func *)pEntry->pUserData; - /* Start collecting function arguments */ - SySetReset(&aArg); - while( pArg < pTos ){ - SySetPut(&aArg, (const void *)&pArg); - pArg++; - } - /* Assume a null return value */ - jx9MemObjInit(&(*pVm), &sRet); - /* Init the call context */ - VmInitCallContext(&sCtx, &(*pVm), pFunc, &sRet, 0); - /* Call the foreign function */ - rc = pFunc->xFunc(&sCtx, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg)); - /* Release the call context */ - VmReleaseCallContext(&sCtx); - if( rc == JX9_ABORT ){ - goto Abort; - } - if( pInstr->iP1 > 0 ){ - /* Pop function name and arguments */ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Save foreign function return value */ - jx9MemObjStore(&sRet, pTos); - jx9MemObjRelease(&sRet); - } - break; - } -/* - * OP_CONSUME: P1 * * - * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack. - */ -case JX9_OP_CONSUME: { - jx9_output_consumer *pCons = &pVm->sVmConsumer; - jx9_value *pCur, *pOut = pTos; - - pOut = &pTos[-pInstr->iP1 + 1]; - pCur = pOut; - /* Start the consume process */ - while( pOut <= pTos ){ - /* Force a string cast */ - if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pOut); - } - if( SyBlobLength(&pOut->sBlob) > 0 ){ - /*SyBlobNullAppend(&pOut->sBlob);*/ - /* Invoke the output consumer callback */ - rc = pCons->xConsumer(SyBlobData(&pOut->sBlob), SyBlobLength(&pOut->sBlob), pCons->pUserData); - /* Increment output length */ - pVm->nOutputLen += SyBlobLength(&pOut->sBlob); - SyBlobRelease(&pOut->sBlob); - if( rc == SXERR_ABORT ){ - /* Output consumer callback request an operation abort. */ - goto Abort; - } - } - pOut++; - } - pTos = &pCur[-1]; - break; - } - - } /* Switch() */ - pc++; /* Next instruction in the stream */ - } /* For(;;) */ -Done: - SySetRelease(&aArg); - return SXRET_OK; -Abort: - SySetRelease(&aArg); - while( pTos >= pStack ){ - jx9MemObjRelease(pTos); - pTos--; - } - return JX9_ABORT; -} -/* - * Execute as much of a local JX9 bytecode program as we can then return. - * This function is a wrapper around [VmByteCodeExec()]. - * See block-comment on that function for additional information. - */ -static sxi32 VmLocalExec(jx9_vm *pVm, SySet *pByteCode,jx9_value *pResult) -{ - jx9_value *pStack; - sxi32 rc; - /* Allocate a new operand stack */ - pStack = VmNewOperandStack(&(*pVm), SySetUsed(pByteCode)); - if( pStack == 0 ){ - return SXERR_MEM; - } - /* Execute the program */ - rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pByteCode), pStack, -1, &(*pResult)); - /* Free the operand stack */ - SyMemBackendFree(&pVm->sAllocator, pStack); - /* Execution result */ - return rc; -} -/* - * Execute as much of a JX9 bytecode program as we can then return. - * This function is a wrapper around [VmByteCodeExec()]. - * See block-comment on that function for additional information. - */ -JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm) -{ - /* Make sure we are ready to execute this program */ - if( pVm->nMagic != JX9_VM_RUN ){ - return pVm->nMagic == JX9_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */ - } - /* Set the execution magic number */ - pVm->nMagic = JX9_VM_EXEC; - /* Execute the program */ - VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pVm->pByteContainer), pVm->aOps, -1, &pVm->sExec); - /* - * TICKET 1433-100: Do not remove the JX9_VM_EXEC magic number - * so that any following call to [jx9_vm_exec()] without calling - * [jx9_vm_reset()] first would fail. - */ - return SXRET_OK; -} -/* - * Extract a memory object (i.e. a variable) from the running script. - * This function must be called after calling jx9_vm_exec(). Otherwise - * NULL is returned. - */ -JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar) -{ - jx9_value *pValue; - if( pVm->nMagic != JX9_VM_EXEC ){ - /* call jx9_vm_exec() first */ - return 0; - } - /* Perform the lookup */ - pValue = VmExtractMemObj(pVm,pVar,FALSE,FALSE); - /* Lookup result */ - return pValue; -} -/* - * Invoke the installed VM output consumer callback to consume - * the desired message. - * Refer to the implementation of [jx9_context_output()] defined - * in 'api.c' for additional information. - */ -JX9_PRIVATE sxi32 jx9VmOutputConsume( - jx9_vm *pVm, /* Target VM */ - SyString *pString /* Message to output */ - ) -{ - jx9_output_consumer *pCons = &pVm->sVmConsumer; - sxi32 rc = SXRET_OK; - /* Call the output consumer */ - if( pString->nByte > 0 ){ - rc = pCons->xConsumer((const void *)pString->zString, pString->nByte, pCons->pUserData); - /* Increment output length */ - pVm->nOutputLen += pString->nByte; - } - return rc; -} -/* - * Format a message and invoke the installed VM output consumer - * callback to consume the formatted message. - * Refer to the implementation of [jx9_context_output_format()] defined - * in 'api.c' for additional information. - */ -JX9_PRIVATE sxi32 jx9VmOutputConsumeAp( - jx9_vm *pVm, /* Target VM */ - const char *zFormat, /* Formatted message to output */ - va_list ap /* Variable list of arguments */ - ) -{ - jx9_output_consumer *pCons = &pVm->sVmConsumer; - sxi32 rc = SXRET_OK; - SyBlob sWorker; - /* Format the message and call the output consumer */ - SyBlobInit(&sWorker, &pVm->sAllocator); - SyBlobFormatAp(&sWorker, zFormat, ap); - if( SyBlobLength(&sWorker) > 0 ){ - /* Consume the formatted message */ - rc = pCons->xConsumer(SyBlobData(&sWorker), SyBlobLength(&sWorker), pCons->pUserData); - } - /* Increment output length */ - pVm->nOutputLen += SyBlobLength(&sWorker); - /* Release the working buffer */ - SyBlobRelease(&sWorker); - return rc; -} -/* - * Return a string representation of the given JX9 OP code. - * This function never fail and always return a pointer - * to a null terminated string. - */ -static const char * VmInstrToString(sxi32 nOp) -{ - const char *zOp = "Unknown "; - switch(nOp){ - case JX9_OP_DONE: zOp = "DONE "; break; - case JX9_OP_HALT: zOp = "HALT "; break; - case JX9_OP_LOAD: zOp = "LOAD "; break; - case JX9_OP_LOADC: zOp = "LOADC "; break; - case JX9_OP_LOAD_MAP: zOp = "LOAD_MAP "; break; - case JX9_OP_LOAD_IDX: zOp = "LOAD_IDX "; break; - case JX9_OP_NOOP: zOp = "NOOP "; break; - case JX9_OP_JMP: zOp = "JMP "; break; - case JX9_OP_JZ: zOp = "JZ "; break; - case JX9_OP_JNZ: zOp = "JNZ "; break; - case JX9_OP_POP: zOp = "POP "; break; - case JX9_OP_CAT: zOp = "CAT "; break; - case JX9_OP_CVT_INT: zOp = "CVT_INT "; break; - case JX9_OP_CVT_STR: zOp = "CVT_STR "; break; - case JX9_OP_CVT_REAL: zOp = "CVT_REAL "; break; - case JX9_OP_CALL: zOp = "CALL "; break; - case JX9_OP_UMINUS: zOp = "UMINUS "; break; - case JX9_OP_UPLUS: zOp = "UPLUS "; break; - case JX9_OP_BITNOT: zOp = "BITNOT "; break; - case JX9_OP_LNOT: zOp = "LOGNOT "; break; - case JX9_OP_MUL: zOp = "MUL "; break; - case JX9_OP_DIV: zOp = "DIV "; break; - case JX9_OP_MOD: zOp = "MOD "; break; - case JX9_OP_ADD: zOp = "ADD "; break; - case JX9_OP_SUB: zOp = "SUB "; break; - case JX9_OP_SHL: zOp = "SHL "; break; - case JX9_OP_SHR: zOp = "SHR "; break; - case JX9_OP_LT: zOp = "LT "; break; - case JX9_OP_LE: zOp = "LE "; break; - case JX9_OP_GT: zOp = "GT "; break; - case JX9_OP_GE: zOp = "GE "; break; - case JX9_OP_EQ: zOp = "EQ "; break; - case JX9_OP_NEQ: zOp = "NEQ "; break; - case JX9_OP_TEQ: zOp = "TEQ "; break; - case JX9_OP_TNE: zOp = "TNE "; break; - case JX9_OP_BAND: zOp = "BITAND "; break; - case JX9_OP_BXOR: zOp = "BITXOR "; break; - case JX9_OP_BOR: zOp = "BITOR "; break; - case JX9_OP_LAND: zOp = "LOGAND "; break; - case JX9_OP_LOR: zOp = "LOGOR "; break; - case JX9_OP_LXOR: zOp = "LOGXOR "; break; - case JX9_OP_STORE: zOp = "STORE "; break; - case JX9_OP_STORE_IDX: zOp = "STORE_IDX "; break; - case JX9_OP_PULL: zOp = "PULL "; break; - case JX9_OP_SWAP: zOp = "SWAP "; break; - case JX9_OP_YIELD: zOp = "YIELD "; break; - case JX9_OP_CVT_BOOL: zOp = "CVT_BOOL "; break; - case JX9_OP_CVT_NULL: zOp = "CVT_NULL "; break; - case JX9_OP_CVT_ARRAY: zOp = "CVT_JSON "; break; - case JX9_OP_CVT_NUMC: zOp = "CVT_NUMC "; break; - case JX9_OP_INCR: zOp = "INCR "; break; - case JX9_OP_DECR: zOp = "DECR "; break; - case JX9_OP_ADD_STORE: zOp = "ADD_STORE "; break; - case JX9_OP_SUB_STORE: zOp = "SUB_STORE "; break; - case JX9_OP_MUL_STORE: zOp = "MUL_STORE "; break; - case JX9_OP_DIV_STORE: zOp = "DIV_STORE "; break; - case JX9_OP_MOD_STORE: zOp = "MOD_STORE "; break; - case JX9_OP_CAT_STORE: zOp = "CAT_STORE "; break; - case JX9_OP_SHL_STORE: zOp = "SHL_STORE "; break; - case JX9_OP_SHR_STORE: zOp = "SHR_STORE "; break; - case JX9_OP_BAND_STORE: zOp = "BAND_STORE "; break; - case JX9_OP_BOR_STORE: zOp = "BOR_STORE "; break; - case JX9_OP_BXOR_STORE: zOp = "BXOR_STORE "; break; - case JX9_OP_CONSUME: zOp = "CONSUME "; break; - case JX9_OP_MEMBER: zOp = "MEMBER "; break; - case JX9_OP_UPLINK: zOp = "UPLINK "; break; - case JX9_OP_SWITCH: zOp = "SWITCH "; break; - case JX9_OP_FOREACH_INIT: - zOp = "4EACH_INIT "; break; - case JX9_OP_FOREACH_STEP: - zOp = "4EACH_STEP "; break; - default: - break; - } - return zOp; -} -/* - * Dump JX9 bytecodes instructions to a human readable format. - * The xConsumer() callback which is an used defined function - * is responsible of consuming the generated dump. - */ -JX9_PRIVATE sxi32 jx9VmDump( - jx9_vm *pVm, /* Target VM */ - ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */ - void *pUserData /* Last argument to xConsumer() */ - ) -{ - sxi32 rc; - rc = VmByteCodeDump(pVm->pByteContainer, xConsumer, pUserData); - return rc; -} -/* - * Default constant expansion callback used by the 'const' statement if used - * outside a object body [i.e: global or function scope]. - * Refer to the implementation of [JX9_CompileConstant()] defined - * in 'compile.c' for additional information. - */ -JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData) -{ - SySet *pByteCode = (SySet *)pUserData; - /* Evaluate and expand constant value */ - VmLocalExec((jx9_vm *)SySetGetUserData(pByteCode), pByteCode, (jx9_value *)pVal); -} -/* - * Section: - * Function handling functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * int func_num_args(void) - * Returns the number of arguments passed to the function. - * Parameters - * None. - * Return - * Total number of arguments passed into the current user-defined function - * or -1 if called from the globe scope. - */ -static int vm_builtin_func_num_args(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - VmFrame *pFrame; - jx9_vm *pVm; - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Current frame */ - pFrame = pVm->pFrame; - if( pFrame->pParent == 0 ){ - SXUNUSED(nArg); - SXUNUSED(apArg); - /* Global frame, return -1 */ - jx9_result_int(pCtx, -1); - return SXRET_OK; - } - /* Total number of arguments passed to the enclosing function */ - nArg = (int)SySetUsed(&pFrame->sArg); - jx9_result_int(pCtx, nArg); - return SXRET_OK; -} -/* - * value func_get_arg(int $arg_num) - * Return an item from the argument list. - * Parameters - * Argument number(index start from zero). - * Return - * Returns the specified argument or FALSE on error. - */ -static int vm_builtin_func_get_arg(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pObj = 0; - VmSlot *pSlot = 0; - VmFrame *pFrame; - jx9_vm *pVm; - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Current frame */ - pFrame = pVm->pFrame; - if( nArg < 1 || pFrame->pParent == 0 ){ - /* Global frame or Missing arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope"); - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Extract the desired index */ - nArg = jx9_value_to_int(apArg[0]); - if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){ - /* Invalid index, return FALSE */ - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Extract the desired argument */ - if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0 ){ - if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0 ){ - /* Return the desired argument */ - jx9_result_value(pCtx, (jx9_value *)pObj); - }else{ - /* No such argument, return false */ - jx9_result_bool(pCtx, 0); - } - }else{ - /* CAN'T HAPPEN */ - jx9_result_bool(pCtx, 0); - } - return SXRET_OK; -} -/* - * array func_get_args(void) - * Returns an array comprising a copy of function's argument list. - * Parameters - * None. - * Return - * Returns an array in which each element is a copy of the corresponding - * member of the current user-defined function's argument list. - * Otherwise FALSE is returned on failure. - */ -static int vm_builtin_func_get_args(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pObj = 0; - jx9_value *pArray; - VmFrame *pFrame; - VmSlot *aSlot; - sxu32 n; - /* Point to the current frame */ - pFrame = pCtx->pVm->pFrame; - if( pFrame->pParent == 0 ){ - /* Global frame, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope"); - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Start filling the array with the given arguments */ - aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); - for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){ - pObj = (jx9_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx); - if( pObj ){ - jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pObj); - } - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return SXRET_OK; -} -/* - * bool function_exists(string $name) - * Return TRUE if the given function has been defined. - * Parameters - * The name of the desired function. - * Return - * Return TRUE if the given function has been defined.False otherwise - */ -static int vm_builtin_func_exists(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zName; - jx9_vm *pVm; - int nLen; - int res; - if( nArg < 1 ){ - /* Missing argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Extract the function name */ - zName = jx9_value_to_string(apArg[0], &nLen); - /* Assume the function is not defined */ - res = 0; - /* Perform the lookup */ - if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 || - SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){ - /* Function is defined */ - res = 1; - } - jx9_result_bool(pCtx, res); - return SXRET_OK; -} -/* - * Verify that the contents of a variable can be called as a function. - * [i.e: Whether it is callable or not]. - * Return TRUE if callable.FALSE otherwise. - */ -JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue) -{ - int res = 0; - if( pValue->iFlags & MEMOBJ_STRING ){ - const char *zName; - int nLen; - /* Extract the name */ - zName = jx9_value_to_string(pValue, &nLen); - /* Perform the lookup */ - if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 || - SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){ - /* Function is callable */ - res = 1; - } - } - return res; -} -/* - * bool is_callable(callable $name[, bool $syntax_only = false]) - * Verify that the contents of a variable can be called as a function. - * Parameters - * $name - * The callback function to check - * $syntax_only - * If set to TRUE the function only verifies that name might be a function or method. - * It will only reject simple variables that are not strings, or an array that does - * not have a valid structure to be used as a callback. The valid ones are supposed - * to have only 2 entries, the first of which is an object or a string, and the second - * a string. - * Return - * TRUE if name is callable, FALSE otherwise. - */ -static int vm_builtin_is_callable(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vm *pVm; - int res; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Perform the requested operation */ - res = jx9VmIsCallable(pVm, apArg[0]); - jx9_result_bool(pCtx, res); - return SXRET_OK; -} -/* - * Hash walker callback used by the [get_defined_functions()] function - * defined below. - */ -static int VmHashFuncStep(SyHashEntry *pEntry, void *pUserData) -{ - jx9_value *pArray = (jx9_value *)pUserData; - jx9_value sName; - sxi32 rc; - /* Prepare the function name for insertion */ - jx9MemObjInitFromString(pArray->pVm, &sName, 0); - jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen); - /* Perform the insertion */ - rc = jx9_array_add_elem(pArray, 0/* Automatic index assign */, &sName); /* Will make it's own copy */ - jx9MemObjRelease(&sName); - return rc; -} -/* - * array get_defined_functions(void) - * Returns an array of all defined functions. - * Parameter - * None. - * Return - * Returns an multidimensional array containing a list of all defined functions - * both built-in (internal) and user-defined. - * The internal functions will be accessible via $arr["internal"], and the user - * defined ones using $arr["user"]. - * Note: - * NULL is returned on failure. - */ -static int vm_builtin_get_defined_func(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray; - /* NOTE: - * Don't worry about freeing memory here, every allocated resource will be released - * automatically by the engine as soon we return from this foreign function. - */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* Return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* Fill with the appropriate information */ - SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pArray); - /* Fill with the appropriate information */ - SyHashForEach(&pCtx->pVm->hFunction, VmHashFuncStep,pArray); - /* Return a copy of the array array */ - jx9_result_value(pCtx, pArray); - return SXRET_OK; -} -/* - * Call a user defined or foreign function where the name of the function - * is stored in the pFunc parameter and the given arguments are stored - * in the apArg[] array. - * Return SXRET_OK if the function was successfuly called.Any other - * return value indicates failure. - */ -JX9_PRIVATE sxi32 jx9VmCallUserFunction( - jx9_vm *pVm, /* Target VM */ - jx9_value *pFunc, /* Callback name */ - int nArg, /* Total number of given arguments */ - jx9_value **apArg, /* Callback arguments */ - jx9_value *pResult /* Store callback return value here. NULL otherwise */ - ) -{ - jx9_value *aStack; - VmInstr aInstr[2]; - int i; - if((pFunc->iFlags & (MEMOBJ_STRING)) == 0 ){ - /* Don't bother processing, it's invalid anyway */ - if( pResult ){ - /* Assume a null return value */ - jx9MemObjRelease(pResult); - } - return SXERR_INVALID; - } - /* Create a new operand stack */ - aStack = VmNewOperandStack(&(*pVm), 1+nArg); - if( aStack == 0 ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, - "JX9 is running out of memory while invoking user callback"); - if( pResult ){ - /* Assume a null return value */ - jx9MemObjRelease(pResult); - } - return SXERR_MEM; - } - /* Fill the operand stack with the given arguments */ - for( i = 0 ; i < nArg ; i++ ){ - jx9MemObjLoad(apArg[i], &aStack[i]); - aStack[i].nIdx = apArg[i]->nIdx; - } - /* Push the function name */ - jx9MemObjLoad(pFunc, &aStack[i]); - aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */ - /* Emit the CALL istruction */ - aInstr[0].iOp = JX9_OP_CALL; - aInstr[0].iP1 = nArg; /* Total number of given arguments */ - aInstr[0].iP2 = 0; - aInstr[0].p3 = 0; - /* Emit the DONE instruction */ - aInstr[1].iOp = JX9_OP_DONE; - aInstr[1].iP1 = 1; /* Extract function return value if available */ - aInstr[1].iP2 = 0; - aInstr[1].p3 = 0; - /* Execute the function body (if available) */ - VmByteCodeExec(&(*pVm), aInstr, aStack, nArg, pResult); - /* Clean up the mess left behind */ - SyMemBackendFree(&pVm->sAllocator, aStack); - return JX9_OK; -} -/* - * Call a user defined or foreign function whith a varibale number - * of arguments where the name of the function is stored in the pFunc - * parameter. - * Return SXRET_OK if the function was successfuly called.Any other - * return value indicates failure. - */ -JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp( - jx9_vm *pVm, /* Target VM */ - jx9_value *pFunc, /* Callback name */ - jx9_value *pResult, /* Store callback return value here. NULL otherwise */ - ... /* 0 (Zero) or more Callback arguments */ - ) -{ - jx9_value *pArg; - SySet aArg; - va_list ap; - sxi32 rc; - SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *)); - /* Copy arguments one after one */ - va_start(ap, pResult); - for(;;){ - pArg = va_arg(ap, jx9_value *); - if( pArg == 0 ){ - break; - } - SySetPut(&aArg, (const void *)&pArg); - } - /* Call the core routine */ - rc = jx9VmCallUserFunction(&(*pVm), pFunc, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg), pResult); - /* Cleanup */ - SySetRelease(&aArg); - return rc; -} -/* - * bool defined(string $name) - * Checks whether a given named constant exists. - * Parameter: - * Name of the desired constant. - * Return - * TRUE if the given constant exists.FALSE otherwise. - */ -static int vm_builtin_defined(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zName; - int nLen = 0; - int res = 0; - if( nArg < 1 ){ - /* Missing constant name, return FALSE */ - jx9_context_throw_error(pCtx,JX9_CTX_NOTICE,"Missing constant name"); - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Extract constant name */ - zName = jx9_value_to_string(apArg[0], &nLen); - /* Perform the lookup */ - if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant, (const void *)zName, (sxu32)nLen) != 0 ){ - /* Already defined */ - res = 1; - } - jx9_result_bool(pCtx, res); - return SXRET_OK; -} -/* - * Hash walker callback used by the [get_defined_constants()] function - * defined below. - */ -static int VmHashConstStep(SyHashEntry *pEntry, void *pUserData) -{ - jx9_value *pArray = (jx9_value *)pUserData; - jx9_value sName; - sxi32 rc; - /* Prepare the constant name for insertion */ - jx9MemObjInitFromString(pArray->pVm, &sName, 0); - jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen); - /* Perform the insertion */ - rc = jx9_array_add_elem(pArray, 0, &sName); /* Will make it's own copy */ - jx9MemObjRelease(&sName); - return rc; -} -/* - * array get_defined_constants(void) - * Returns an associative array with the names of all defined - * constants. - * Parameters - * NONE. - * Returns - * Returns the names of all the constants currently defined. - */ -static int vm_builtin_get_defined_constants(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray; - /* Create the array first*/ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* Return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* Fill the array with the defined constants */ - SyHashForEach(&pCtx->pVm->hConstant, VmHashConstStep, pArray); - /* Return the created array */ - jx9_result_value(pCtx, pArray); - return SXRET_OK; -} -/* - * Section: - * Random numbers/string generators. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * Generate a random 32-bit unsigned integer. - * JX9 use it's own private PRNG which is based on the one - * used by te SQLite3 library. - */ -JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm) -{ - sxu32 iNum; - SyRandomness(&pVm->sPrng, (void *)&iNum, sizeof(sxu32)); - return iNum; -} -/* - * Generate a random string (English Alphabet) of length nLen. - * Note that the generated string is NOT null terminated. - * JX9 use it's own private PRNG which is based on the one used - * by te SQLite3 library. - */ -JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen) -{ - static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */ - int i; - /* Generate a binary string first */ - SyRandomness(&pVm->sPrng, zBuf, (sxu32)nLen); - /* Turn the binary string into english based alphabet */ - for( i = 0 ; i < nLen ; ++i ){ - zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)]; - } -} -/* - * int rand() - * Generate a random (unsigned 32-bit) integer. - * Parameter - * $min - * The lowest value to return (default: 0) - * $max - * The highest value to return (default: getrandmax()) - * Return - * A pseudo random value between min (or 0) and max (or getrandmax(), inclusive). - * Note: - * JX9 use it's own private PRNG which is based on the one used - * by te SQLite3 library. - */ -static int vm_builtin_rand(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - sxu32 iNum; - /* Generate the random number */ - iNum = jx9VmRandomNum(pCtx->pVm); - if( nArg > 1 ){ - sxu32 iMin, iMax; - iMin = (sxu32)jx9_value_to_int(apArg[0]); - iMax = (sxu32)jx9_value_to_int(apArg[1]); - if( iMin < iMax ){ - sxu32 iDiv = iMax+1-iMin; - if( iDiv > 0 ){ - iNum = (iNum % iDiv)+iMin; - } - }else if(iMax > 0 ){ - iNum %= iMax; - } - } - /* Return the number */ - jx9_result_int64(pCtx, (jx9_int64)iNum); - return SXRET_OK; -} -/* - * int getrandmax(void) - * Show largest possible random value - * Return - * The largest possible random value returned by rand() which is in - * this implementation 0xFFFFFFFF. - * Note: - * JX9 use it's own private PRNG which is based on the one used - * by te SQLite3 library. - */ -static int vm_builtin_getrandmax(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - jx9_result_int64(pCtx, SXU32_HIGH); - return SXRET_OK; -} -/* - * string rand_str() - * string rand_str(int $len) - * Generate a random string (English alphabet). - * Parameter - * $len - * Length of the desired string (default: 16, Min: 1, Max: 1024) - * Return - * A pseudo random string. - * Note: - * JX9 use it's own private PRNG which is based on the one used - * by te SQLite3 library. - */ -static int vm_builtin_rand_str(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - char zString[1024]; - int iLen = 0x10; - if( nArg > 0 ){ - /* Get the desired length */ - iLen = jx9_value_to_int(apArg[0]); - if( iLen < 1 || iLen > 1024 ){ - /* Default length */ - iLen = 0x10; - } - } - /* Generate the random string */ - jx9VmRandomString(pCtx->pVm, zString, iLen); - /* Return the generated string */ - jx9_result_string(pCtx, zString, iLen); /* Will make it's own copy */ - return SXRET_OK; -} -/* - * Section: - * Language construct implementation as foreign functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * void print($string...) - * Output one or more messages. - * Parameters - * $string - * Message to output. - * Return - * NULL. - */ -static int vm_builtin_print(jx9_context *pCtx, int nArg,jx9_value **apArg) -{ - const char *zData; - int nDataLen = 0; - jx9_vm *pVm; - int i, rc; - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Output */ - for( i = 0 ; i < nArg ; ++i ){ - zData = jx9_value_to_string(apArg[i], &nDataLen); - if( nDataLen > 0 ){ - rc = pVm->sVmConsumer.xConsumer((const void *)zData, (unsigned int)nDataLen, pVm->sVmConsumer.pUserData); - /* Increment output length */ - pVm->nOutputLen += nDataLen; - if( rc == SXERR_ABORT ){ - /* Output consumer callback request an operation abort */ - return JX9_ABORT; - } - } - } - return SXRET_OK; -} -/* - * void exit(string $msg) - * void exit(int $status) - * void die(string $ms) - * void die(int $status) - * Output a message and terminate program execution. - * Parameter - * If status is a string, this function prints the status just before exiting. - * If status is an integer, that value will be used as the exit status - * and not printed - * Return - * NULL - */ -static int vm_builtin_exit(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg > 0 ){ - if( jx9_value_is_string(apArg[0]) ){ - const char *zData; - int iLen = 0; - /* Print exit message */ - zData = jx9_value_to_string(apArg[0], &iLen); - jx9_context_output(pCtx, zData, iLen); - }else if(jx9_value_is_int(apArg[0]) ){ - sxi32 iExitStatus; - /* Record exit status code */ - iExitStatus = jx9_value_to_int(apArg[0]); - pCtx->pVm->iExitStatus = iExitStatus; - } - } - /* Abort processing immediately */ - return JX9_ABORT; -} -/* - * Unset a memory object [i.e: a jx9_value]. - */ -JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm,sxu32 nObjIdx) -{ - jx9_value *pObj; - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nObjIdx); - if( pObj ){ - VmSlot sFree; - /* Release the object */ - jx9MemObjRelease(pObj); - /* Restore to the free list */ - sFree.nIdx = nObjIdx; - sFree.pUserData = 0; - SySetPut(&pVm->aFreeObj, (const void *)&sFree); - } - return SXRET_OK; -} -/* - * string gettype($var) - * Get the type of a variable - * Parameters - * $var - * The variable being type checked. - * Return - * String representation of the given variable type. - */ -static int vm_builtin_gettype(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zType = "null"; - if( nArg > 0 ){ - zType = jx9MemObjTypeDump(apArg[0]); - } - /* Return the variable type */ - jx9_result_string(pCtx, zType, -1/*Compute length automatically*/); - return SXRET_OK; -} -/* - * string get_resource_type(resource $handle) - * This function gets the type of the given resource. - * Parameters - * $handle - * The evaluated resource handle. - * Return - * If the given handle is a resource, this function will return a string - * representing its type. If the type is not identified by this function - * the return value will be the string Unknown. - * This function will return FALSE and generate an error if handle - * is not a resource. - */ -static int vm_builtin_get_resource_type(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE*/ - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - jx9_result_string_format(pCtx, "resID_%#x", apArg[0]->x.pOther); - return SXRET_OK; -} -/* - * void dump(expression, ....) - * dump — Dumps information about a variable - * Parameters - * One or more expression to dump. - * Returns - * Nothing. - */ -static int vm_builtin_dump(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyBlob sDump; /* Generated dump is stored here */ - int i; - SyBlobInit(&sDump,&pCtx->pVm->sAllocator); - /* Dump one or more expressions */ - for( i = 0 ; i < nArg ; i++ ){ - jx9_value *pObj = apArg[i]; - /* Reset the working buffer */ - SyBlobReset(&sDump); - /* Dump the given expression */ - jx9MemObjDump(&sDump,pObj); - /* Output */ - if( SyBlobLength(&sDump) > 0 ){ - jx9_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump)); - } - } - /* Release the working buffer */ - SyBlobRelease(&sDump); - return SXRET_OK; -} -/* - * Section: - * Version, Credits and Copyright related functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Stable. - */ -/* - * string jx9_version(void) - * string jx9_credits(void) - * Returns the running version of the jx9 version. - * Parameters - * None - * Return - * Current jx9 version. - */ -static int vm_builtin_jx9_version(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SXUNUSED(nArg); - SXUNUSED(apArg); /* cc warning */ - /* Current engine version, signature and cipyright notice */ - jx9_result_string_format(pCtx,"%s %s, %s",JX9_VERSION,JX9_SIG,JX9_COPYRIGHT); - return JX9_OK; -} -/* - * Section: - * URL related routines. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* Forward declaration */ -static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen); -/* - * value parse_url(string $url [, int $component = -1 ]) - * Parse a URL and return its fields. - * Parameters - * $url - * The URL to parse. - * $component - * Specify one of JX9_URL_SCHEME, JX9_URL_HOST, JX9_URL_PORT, JX9_URL_USER - * JX9_URL_PASS, JX9_URL_PATH, JX9_URL_QUERY or JX9_URL_FRAGMENT to retrieve - * just a specific URL component as a string (except when JX9_URL_PORT is given - * in which case the return value will be an integer). - * Return - * If the component parameter is omitted, an associative array is returned. - * At least one element will be present within the array. Potential keys within - * this array are: - * scheme - e.g. http - * host - * port - * user - * pass - * path - * query - after the question mark ? - * fragment - after the hashmark # - * Note: - * FALSE is returned on failure. - * This function work with relative URL unlike the one shipped - * with the standard JX9 engine. - */ -static int vm_builtin_parse_url(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zStr; /* Input string */ - SyString *pComp; /* Pointer to the URI component */ - SyhttpUri sURI; /* Parse of the given URI */ - int nLen; - sxi32 rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the given URI */ - zStr = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Nothing to process, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Get a parse */ - rc = VmHttpSplitURI(&sURI, zStr, (sxu32)nLen); - if( rc != SXRET_OK ){ - /* Malformed input, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( nArg > 1 ){ - int nComponent = jx9_value_to_int(apArg[1]); - /* Refer to constant.c for constants values */ - switch(nComponent){ - case 1: /* JX9_URL_SCHEME */ - pComp = &sURI.sScheme; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 2: /* JX9_URL_HOST */ - pComp = &sURI.sHost; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 3: /* JX9_URL_PORT */ - pComp = &sURI.sPort; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - int iPort = 0; - /* Cast the value to integer */ - SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0); - jx9_result_int(pCtx, iPort); - } - break; - case 4: /* JX9_URL_USER */ - pComp = &sURI.sUser; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 5: /* JX9_URL_PASS */ - pComp = &sURI.sPass; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 7: /* JX9_URL_QUERY */ - pComp = &sURI.sQuery; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 8: /* JX9_URL_FRAGMENT */ - pComp = &sURI.sFragment; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 6: /* JX9_URL_PATH */ - pComp = &sURI.sPath; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - default: - /* No such entry, return NULL */ - jx9_result_null(pCtx); - break; - } - }else{ - jx9_value *pArray, *pValue; - /* Return an associative array */ - pArray = jx9_context_new_array(pCtx); /* Empty array */ - pValue = jx9_context_new_scalar(pCtx); /* Array value */ - if( pArray == 0 || pValue == 0 ){ - /* Out of memory */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "jx9 engine is running out of memory"); - /* Return false */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Fill the array */ - pComp = &sURI.sScheme; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "scheme", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sHost; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "host", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sPort; - if( pComp->nByte > 0 ){ - int iPort = 0;/* cc warning */ - /* Convert to integer */ - SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0); - jx9_value_int(pValue, iPort); - jx9_array_add_strkey_elem(pArray, "port", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sUser; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "user", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sPass; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "pass", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sPath; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "path", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sQuery; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "query", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sFragment; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "fragment", pValue); /* Will make it's own copy */ - } - /* Return the created array */ - jx9_result_value(pCtx, pArray); - /* NOTE: - * Don't worry about freeing 'pValue', everything will be released - * automatically as soon we return from this function. - */ - } - /* All done */ - return JX9_OK; -} -/* - * Section: - * Array related routines. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - * Note 2012-5-21 01:04:15: - * Array related functions that need access to the underlying - * virtual machine are implemented here rather than 'hashmap.c' - */ -/* - * The [extract()] function store it's state information in an instance - * of the following structure. - */ -typedef struct extract_aux_data extract_aux_data; -struct extract_aux_data -{ - jx9_vm *pVm; /* VM that own this instance */ - int iCount; /* Number of variables successfully imported */ - const char *zPrefix; /* Prefix name */ - int Prefixlen; /* Prefix length */ - int iFlags; /* Control flags */ - char zWorker[1024]; /* Working buffer */ -}; -/* Forward declaration */ -static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData); -/* - * int extract(array $var_array[, int $extract_type = EXTR_OVERWRITE[, string $prefix = NULL ]]) - * Import variables into the current symbol table from an array. - * Parameters - * $var_array - * An associative array. This function treats keys as variable names and values - * as variable values. For each key/value pair it will create a variable in the current symbol - * table, subject to extract_type and prefix parameters. - * You must use an associative array; a numerically indexed array will not produce results - * unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID. - * $extract_type - * The way invalid/numeric keys and collisions are treated is determined by the extract_type. - * It can be one of the following values: - * EXTR_OVERWRITE - * If there is a collision, overwrite the existing variable. - * EXTR_SKIP - * If there is a collision, don't overwrite the existing variable. - * EXTR_PREFIX_SAME - * If there is a collision, prefix the variable name with prefix. - * EXTR_PREFIX_ALL - * Prefix all variable names with prefix. - * EXTR_PREFIX_INVALID - * Only prefix invalid/numeric variable names with prefix. - * EXTR_IF_EXISTS - * Only overwrite the variable if it already exists in the current symbol table - * otherwise do nothing. - * This is useful for defining a list of valid variables and then extracting only those - * variables you have defined out of $_REQUEST, for example. - * EXTR_PREFIX_IF_EXISTS - * Only create prefixed variable names if the non-prefixed version of the same variable exists in - * the current symbol table. - * $prefix - * Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL - * EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name - * it is not imported into the symbol table. Prefixes are automatically separated from the array key by an - * underscore character. - * Return - * Returns the number of variables successfully imported into the symbol table. - */ -static int vm_builtin_extract(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - extract_aux_data sAux; - jx9_hashmap *pMap; - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing/Invalid arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the target hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Empty map, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Prepare the aux data */ - SyZero(&sAux, sizeof(extract_aux_data)-sizeof(sAux.zWorker)); - if( nArg > 1 ){ - sAux.iFlags = jx9_value_to_int(apArg[1]); - if( nArg > 2 ){ - sAux.zPrefix = jx9_value_to_string(apArg[2], &sAux.Prefixlen); - } - } - sAux.pVm = pCtx->pVm; - /* Invoke the worker callback */ - jx9HashmapWalk(pMap, VmExtractCallback, &sAux); - /* Number of variables successfully imported */ - jx9_result_int(pCtx, sAux.iCount); - return JX9_OK; -} -/* - * Worker callback for the [extract()] function defined - * below. - */ -static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData) -{ - extract_aux_data *pAux = (extract_aux_data *)pUserData; - int iFlags = pAux->iFlags; - jx9_vm *pVm = pAux->pVm; - jx9_value *pObj; - SyString sVar; - if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){ - iFlags |= 0x08; /*EXTR_PREFIX_ALL*/ - } - /* Perform a string cast */ - jx9MemObjToString(pKey); - if( SyBlobLength(&pKey->sBlob) < 1 ){ - /* Unavailable variable name */ - return SXRET_OK; - } - sVar.nByte = 0; /* cc warning */ - if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){ - sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker, sizeof(pAux->zWorker), "%.*s_%.*s", - pAux->Prefixlen, pAux->zPrefix, - SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob) - ); - }else{ - sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob), pAux->zWorker, - SXMIN(SyBlobLength(&pKey->sBlob), sizeof(pAux->zWorker))); - } - sVar.zString = pAux->zWorker; - /* Try to extract the variable */ - pObj = VmExtractMemObj(pVm, &sVar, TRUE, FALSE); - if( pObj ){ - /* Collision */ - if( iFlags & 0x02 /* EXTR_SKIP */ ){ - return SXRET_OK; - } - if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){ - if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){ - /* Already prefixed */ - return SXRET_OK; - } - sVar.nByte = SyBufferFormat( - pAux->zWorker, sizeof(pAux->zWorker), - "%.*s_%.*s", - pAux->Prefixlen, pAux->zPrefix, - SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob) - ); - pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE); - } - }else{ - /* Create the variable */ - pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE); - } - if( pObj ){ - /* Overwrite the old value */ - jx9MemObjStore(pValue, pObj); - /* Increment counter */ - pAux->iCount++; - } - return SXRET_OK; -} -/* - * Compile and evaluate a JX9 chunk at run-time. - * Refer to the include language construct implementation for more - * information. - */ -static sxi32 VmEvalChunk( - jx9_vm *pVm, /* Underlying Virtual Machine */ - jx9_context *pCtx, /* Call Context */ - SyString *pChunk, /* JX9 chunk to evaluate */ - int iFlags, /* Compile flag */ - int bTrueReturn /* TRUE to return execution result */ - ) -{ - SySet *pByteCode, aByteCode; - ProcConsumer xErr = 0; - void *pErrData = 0; - /* Initialize bytecode container */ - SySetInit(&aByteCode, &pVm->sAllocator, sizeof(VmInstr)); - SySetAlloc(&aByteCode, 0x20); - /* Reset the code generator */ - if( bTrueReturn ){ - /* Included file, log compile-time errors */ - xErr = pVm->pEngine->xConf.xErr; - pErrData = pVm->pEngine->xConf.pErrData; - } - jx9ResetCodeGenerator(pVm, xErr, pErrData); - /* Swap bytecode container */ - pByteCode = pVm->pByteContainer; - pVm->pByteContainer = &aByteCode; - /* Compile the chunk */ - jx9CompileScript(pVm, pChunk, iFlags); - if( pVm->sCodeGen.nErr > 0 ){ - /* Compilation error, return false */ - if( pCtx ){ - jx9_result_bool(pCtx, 0); - } - }else{ - jx9_value sResult; /* Return value */ - if( SXRET_OK != jx9VmEmitInstr(pVm, JX9_OP_DONE, 0, 0, 0, 0) ){ - /* Out of memory */ - if( pCtx ){ - jx9_result_bool(pCtx, 0); - } - goto Cleanup; - } - if( bTrueReturn ){ - /* Assume a boolean true return value */ - jx9MemObjInitFromBool(pVm, &sResult, 1); - }else{ - /* Assume a null return value */ - jx9MemObjInit(pVm, &sResult); - } - /* Execute the compiled chunk */ - VmLocalExec(pVm, &aByteCode, &sResult); - if( pCtx ){ - /* Set the execution result */ - jx9_result_value(pCtx, &sResult); - } - jx9MemObjRelease(&sResult); - } -Cleanup: - /* Cleanup the mess left behind */ - pVm->pByteContainer = pByteCode; - SySetRelease(&aByteCode); - return SXRET_OK; -} -/* - * Check if a file path is already included. - */ -static int VmIsIncludedFile(jx9_vm *pVm, SyString *pFile) -{ - SyString *aEntries; - sxu32 n; - aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded); - /* Perform a linear search */ - for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){ - if( SyStringCmp(pFile, &aEntries[n], SyMemcmp) == 0 ){ - /* Already included */ - return TRUE; - } - } - return FALSE; -} -/* - * Push a file path in the appropriate VM container. - */ -JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew) -{ - SyString sPath; - char *zDup; -#ifdef __WINNT__ - char *zCur; -#endif - sxi32 rc; - if( nLen < 0 ){ - nLen = SyStrlen(zPath); - } - /* Duplicate the file path first */ - zDup = SyMemBackendStrDup(&pVm->sAllocator, zPath, nLen); - if( zDup == 0 ){ - return SXERR_MEM; - } -#ifdef __WINNT__ - /* Normalize path on windows - * Example: - * Path/To/File.jx9 - * becomes - * path\to\file.jx9 - */ - zCur = zDup; - while( zCur[0] != 0 ){ - if( zCur[0] == '/' ){ - zCur[0] = '\\'; - }else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){ - int c = SyToLower(zCur[0]); - zCur[0] = (char)c; /* MSVC stupidity */ - } - zCur++; - } -#endif - /* Install the file path */ - SyStringInitFromBuf(&sPath, zDup, nLen); - if( !bMain ){ - if( VmIsIncludedFile(&(*pVm), &sPath) ){ - /* Already included */ - *pNew = 0; - }else{ - /* Insert in the corresponding container */ - rc = SySetPut(&pVm->aIncluded, (const void *)&sPath); - if( rc != SXRET_OK ){ - SyMemBackendFree(&pVm->sAllocator, zDup); - return rc; - } - *pNew = 1; - } - } - SySetPut(&pVm->aFiles, (const void *)&sPath); - return SXRET_OK; -} -/* - * Compile and Execute a JX9 script at run-time. - * SXRET_OK is returned on sucessful evaluation.Any other return values - * indicates failure. - * Note that the JX9 script to evaluate can be a local or remote file.In - * either cases the [jx9StreamReadWholeFile()] function handle all the underlying - * operations. - * If the [jJX9_DISABLE_BUILTIN_FUNC] compile-time directive is defined, then - * this function is a no-op. - * Refer to the implementation of the include(), import() language - * constructs for more information. - */ -static sxi32 VmExecIncludedFile( - jx9_context *pCtx, /* Call Context */ - SyString *pPath, /* Script path or URL*/ - int IncludeOnce /* TRUE if called from import() or require_once() */ - ) -{ - sxi32 rc; -#ifndef JX9_DISABLE_BUILTIN_FUNC - const jx9_io_stream *pStream; - SyBlob sContents; - void *pHandle; - jx9_vm *pVm; - int isNew; - /* Initialize fields */ - pVm = pCtx->pVm; - SyBlobInit(&sContents, &pVm->sAllocator); - isNew = 0; - /* Extract the associated stream */ - pStream = jx9VmGetStreamDevice(pVm, &pPath->zString, pPath->nByte); - /* - * Open the file or the URL [i.e: http://jx9.symisc.net/example/hello.jx9.txt"] - * in a read-only mode. - */ - pHandle = jx9StreamOpenHandle(pVm, pStream,pPath->zString, JX9_IO_OPEN_RDONLY, TRUE, 0, TRUE, &isNew); - if( pHandle == 0 ){ - return SXERR_IO; - } - rc = SXRET_OK; /* Stupid cc warning */ - if( IncludeOnce && !isNew ){ - /* Already included */ - rc = SXERR_EXISTS; - }else{ - /* Read the whole file contents */ - rc = jx9StreamReadWholeFile(pHandle, pStream, &sContents); - if( rc == SXRET_OK ){ - SyString sScript; - /* Compile and execute the script */ - SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents)); - VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, 0, TRUE); - } - } - /* Pop from the set of included file */ - (void)SySetPop(&pVm->aFiles); - /* Close the handle */ - jx9StreamCloseHandle(pStream, pHandle); - /* Release the working buffer */ - SyBlobRelease(&sContents); -#else - pCtx = 0; /* cc warning */ - pPath = 0; - IncludeOnce = 0; - rc = SXERR_IO; -#endif /* JX9_DISABLE_BUILTIN_FUNC */ - return rc; -} -/* * include: - * According to the JX9 reference manual. - * The include() function includes and evaluates the specified file. - * Files are included based on the file path given or, if none is given - * the include_path specified.If the file isn't found in the include_path - * include() will finally check in the calling script's own directory - * and the current working directory before failing. The include() - * construct will emit a warning if it cannot find a file; this is different - * behavior from require(), which will emit a fatal error. - * If a path is defined — whether absolute (starting with a drive letter - * or \ on Windows, or / on Unix/Linux systems) or relative to the current - * directory (starting with . or ..) — the include_path will be ignored altogether. - * For example, if a filename begins with ../, the parser will look in the parent - * directory to find the requested file. - * When a file is included, the code it contains inherits the variable scope - * of the line on which the include occurs. Any variables available at that line - * in the calling file will be available within the called file, from that point forward. - * However, all functions and objectes defined in the included file have the global scope. - */ -static int vm_builtin_include(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyString sFile; - sxi32 rc; - if( nArg < 1 ){ - /* Nothing to evaluate, return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* File to include */ - sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte); - if( sFile.nByte < 1 ){ - /* Empty string, return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* Open, compile and execute the desired script */ - rc = VmExecIncludedFile(&(*pCtx), &sFile, FALSE); - if( rc != SXRET_OK ){ - /* Emit a warning and return false */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile); - jx9_result_bool(pCtx, 0); - } - return SXRET_OK; -} -/* - * import: - * According to the JX9 reference manual. - * The import() statement includes and evaluates the specified file during - * the execution of the script. This is a behavior similar to the include() - * statement, with the only difference being that if the code from a file has already - * been included, it will not be included again. As the name suggests, it will be included - * just once. - */ -static int vm_builtin_import(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyString sFile; - sxi32 rc; - if( nArg < 1 ){ - /* Nothing to evaluate, return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* File to include */ - sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte); - if( sFile.nByte < 1 ){ - /* Empty string, return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* Open, compile and execute the desired script */ - rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE); - if( rc == SXERR_EXISTS ){ - /* File already included, return TRUE */ - jx9_result_bool(pCtx, 1); - return SXRET_OK; - } - if( rc != SXRET_OK ){ - /* Emit a warning and return false */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile); - jx9_result_bool(pCtx, 0); - } - return SXRET_OK; -} -/* - * Section: - * Command line arguments processing. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * Check if a short option argument [i.e: -c] is available in the command - * line string. Return a pointer to the start of the stream on success. - * NULL otherwise. - */ -static const char * VmFindShortOpt(int c, const char *zIn, const char *zEnd) -{ - while( zIn < zEnd ){ - if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){ - /* Got one */ - return &zIn[1]; - } - /* Advance the cursor */ - zIn++; - } - /* No such option */ - return 0; -} -/* - * Check if a long option argument [i.e: --opt] is available in the command - * line string. Return a pointer to the start of the stream on success. - * NULL otherwise. - */ -static const char * VmFindLongOpt(const char *zLong, int nByte, const char *zIn, const char *zEnd) -{ - const char *zOpt; - while( zIn < zEnd ){ - if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){ - zIn += 2; - zOpt = zIn; - while( zIn < zEnd && !SyisSpace(zIn[0]) ){ - if( zIn[0] == '=' /* --opt=val */){ - break; - } - zIn++; - } - /* Test */ - if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt, zLong, nByte) == 0 ){ - /* Got one, return it's value */ - return zIn; - } - - }else{ - zIn++; - } - } - /* No such option */ - return 0; -} -/* - * Long option [i.e: --opt] arguments private data structure. - */ -struct getopt_long_opt -{ - const char *zArgIn, *zArgEnd; /* Command line arguments */ - jx9_value *pWorker; /* Worker variable*/ - jx9_value *pArray; /* getopt() return value */ - jx9_context *pCtx; /* Call Context */ -}; -/* Forward declaration */ -static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData); -/* - * Extract short or long argument option values. - */ -static void VmExtractOptArgValue( - jx9_value *pArray, /* getopt() return value */ - jx9_value *pWorker, /* Worker variable */ - const char *zArg, /* Argument stream */ - const char *zArgEnd, /* End of the argument stream */ - int need_val, /* TRUE to fetch option argument */ - jx9_context *pCtx, /* Call Context */ - const char *zName /* Option name */) -{ - jx9_value_bool(pWorker, 0); - if( !need_val ){ - /* - * Option does not need arguments. - * Insert the option name and a boolean FALSE. - */ - jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */ - }else{ - const char *zCur; - /* Extract option argument */ - zArg++; - if( zArg < zArgEnd && zArg[0] == '=' ){ - zArg++; - } - while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ - zArg++; - } - if( zArg >= zArgEnd || zArg[0] == '-' ){ - /* - * Argument not found. - * Insert the option name and a boolean FALSE. - */ - jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */ - return; - } - /* Delimit the value */ - zCur = zArg; - if( zArg[0] == '\'' || zArg[0] == '"' ){ - int d = zArg[0]; - /* Delimt the argument */ - zArg++; - zCur = zArg; - while( zArg < zArgEnd ){ - if( zArg[0] == d && zArg[-1] != '\\' ){ - /* Delimiter found, exit the loop */ - break; - } - zArg++; - } - /* Save the value */ - jx9_value_string(pWorker, zCur, (int)(zArg-zCur)); - if( zArg < zArgEnd ){ zArg++; } - }else{ - while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){ - zArg++; - } - /* Save the value */ - jx9_value_string(pWorker, zCur, (int)(zArg-zCur)); - } - /* - * Check if we are dealing with multiple values. - * If so, create an array to hold them, rather than a scalar variable. - */ - while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ - zArg++; - } - if( zArg < zArgEnd && zArg[0] != '-' ){ - jx9_value *pOptArg; /* Array of option arguments */ - pOptArg = jx9_context_new_array(pCtx); - if( pOptArg == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - }else{ - /* Insert the first value */ - jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */ - for(;;){ - if( zArg >= zArgEnd || zArg[0] == '-' ){ - /* No more value */ - break; - } - /* Delimit the value */ - zCur = zArg; - if( zArg < zArgEnd && zArg[0] == '\\' ){ - zArg++; - zCur = zArg; - } - while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){ - zArg++; - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pWorker); - /* Save the value */ - jx9_value_string(pWorker, zCur, (int)(zArg-zCur)); - /* Insert */ - jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */ - /* Jump trailing white spaces */ - while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ - zArg++; - } - } - /* Insert the option arg array */ - jx9_array_add_strkey_elem(pArray, (const char *)zName, pOptArg); /* Will make it's own copy */ - /* Safely release */ - jx9_context_release_value(pCtx, pOptArg); - } - }else{ - /* Single value */ - jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */ - } - } -} -/* - * array getopt(string $options[, array $longopts ]) - * Gets options from the command line argument list. - * Parameters - * $options - * Each character in this string will be used as option characters - * and matched against options passed to the script starting with - * a single hyphen (-). For example, an option string "x" recognizes - * an option -x. Only a-z, A-Z and 0-9 are allowed. - * $longopts - * An array of options. Each element in this array will be used as option - * strings and matched against options passed to the script starting with - * two hyphens (--). For example, an longopts element "opt" recognizes an - * option --opt. - * Return - * This function will return an array of option / argument pairs or FALSE - * on failure. - */ -static int vm_builtin_getopt(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn, *zEnd, *zArg, *zArgIn, *zArgEnd; - struct getopt_long_opt sLong; - jx9_value *pArray, *pWorker; - SyBlob *pArg; - int nByte; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Missing/Invalid option arguments"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract option arguments */ - zIn = jx9_value_to_string(apArg[0], &nByte); - zEnd = &zIn[nByte]; - /* Point to the string representation of the $argv[] array */ - pArg = &pCtx->pVm->sArgv; - /* Create a new empty array and a worker variable */ - pArray = jx9_context_new_array(pCtx); - pWorker = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pWorker == 0 ){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( SyBlobLength(pArg) < 1 ){ - /* Empty command line, return the empty array*/ - jx9_result_value(pCtx, pArray); - /* Everything will be released automatically when we return - * from this function. - */ - return JX9_OK; - } - zArgIn = (const char *)SyBlobData(pArg); - zArgEnd = &zArgIn[SyBlobLength(pArg)]; - /* Fill the long option structure */ - sLong.pArray = pArray; - sLong.pWorker = pWorker; - sLong.zArgIn = zArgIn; - sLong.zArgEnd = zArgEnd; - sLong.pCtx = pCtx; - /* Start processing */ - while( zIn < zEnd ){ - int c = zIn[0]; - int need_val = 0; - /* Advance the stream cursor */ - zIn++; - /* Ignore non-alphanum characters */ - if( !SyisAlphaNum(c) ){ - continue; - } - if( zIn < zEnd && zIn[0] == ':' ){ - zIn++; - need_val = 1; - if( zIn < zEnd && zIn[0] == ':' ){ - zIn++; - } - } - /* Find option */ - zArg = VmFindShortOpt(c, zArgIn, zArgEnd); - if( zArg == 0 ){ - /* No such option */ - continue; - } - /* Extract option argument value */ - VmExtractOptArgValue(pArray, pWorker, zArg, zArgEnd, need_val, pCtx, (const char *)&c); - } - if( nArg > 1 && jx9_value_is_json_array(apArg[1]) && jx9_array_count(apArg[1]) > 0 ){ - /* Process long options */ - jx9_array_walk(apArg[1], VmProcessLongOpt, &sLong); - } - /* Return the option array */ - jx9_result_value(pCtx, pArray); - /* - * Don't worry about freeing memory, everything will be released - * automatically as soon we return from this foreign function. - */ - return JX9_OK; -} -/* - * Array walker callback used for processing long options values. - */ -static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData) -{ - struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData; - const char *zArg, *zOpt, *zEnd; - int need_value = 0; - int nByte; - /* Value must be of type string */ - if( !jx9_value_is_string(pValue) ){ - /* Simply ignore */ - return JX9_OK; - } - zOpt = jx9_value_to_string(pValue, &nByte); - if( nByte < 1 ){ - /* Empty string, ignore */ - return JX9_OK; - } - zEnd = &zOpt[nByte - 1]; - if( zEnd[0] == ':' ){ - char *zTerm; - /* Try to extract a value */ - need_value = 1; - while( zEnd >= zOpt && zEnd[0] == ':' ){ - zEnd--; - } - if( zOpt >= zEnd ){ - /* Empty string, ignore */ - SXUNUSED(pKey); - return JX9_OK; - } - zEnd++; - zTerm = (char *)zEnd; - zTerm[0] = 0; - }else{ - zEnd = &zOpt[nByte]; - } - /* Find the option */ - zArg = VmFindLongOpt(zOpt, (int)(zEnd-zOpt), pOpt->zArgIn, pOpt->zArgEnd); - if( zArg == 0 ){ - /* No such option, return immediately */ - return JX9_OK; - } - /* Try to extract a value */ - VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt); - return JX9_OK; -} -/* - * int utf8_encode(string $input) - * UTF-8 encoding. - * This function encodes the string data to UTF-8, and returns the encoded version. - * UTF-8 is a standard mechanism used by Unicode for encoding wide character values - * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized - * (meaning it is possible for a program to figure out where in the bytestream characters start) - * and can be used with normal string comparison functions for sorting and such. - * Notes on UTF-8 (According to SQLite3 authors): - * Byte-0 Byte-1 Byte-2 Byte-3 Value - * 0xxxxxxx 00000000 00000000 0xxxxxxx - * 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx - * 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx - * 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx - * Parameters - * $input - * String to encode or NULL on failure. - * Return - * An UTF-8 encoded string. - */ -static int vm_builtin_utf8_encode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nByte, c, e; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte); - if( nByte < 1 ){ - /* Empty string, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - zEnd = &zIn[nByte]; - /* Start the encoding process */ - for(;;){ - if( zIn >= zEnd ){ - /* End of input */ - break; - } - c = zIn[0]; - /* Advance the stream cursor */ - zIn++; - /* Encode */ - if( c<0x00080 ){ - e = (c&0xFF); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - }else if( c<0x00800 ){ - e = 0xC0 + ((c>>6)&0x1F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + (c & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - }else if( c<0x10000 ){ - e = 0xE0 + ((c>>12)&0x0F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + ((c>>6) & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + (c & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - }else{ - e = 0xF0 + ((c>>18) & 0x07); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + ((c>>12) & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + ((c>>6) & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + (c & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - } - } - /* All done */ - return JX9_OK; -} -/* - * UTF-8 decoding routine extracted from the sqlite3 source tree. - * Original author: D. Richard Hipp (http://www.sqlite.org) - * Status: Public Domain - */ -/* -** This lookup table is used to help decode the first byte of -** a multi-byte UTF8 character. -*/ -static const unsigned char UtfTrans1[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, -}; -/* -** Translate a single UTF-8 character. Return the unicode value. -** -** During translation, assume that the byte that zTerm points -** is a 0x00. -** -** Write a pointer to the next unread byte back into *pzNext. -** -** Notes On Invalid UTF-8: -** -** * This routine never allows a 7-bit character (0x00 through 0x7f) to -** be encoded as a multi-byte character. Any multi-byte character that -** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. -** -** * This routine never allows a UTF16 surrogate value to be encoded. -** If a multi-byte character attempts to encode a value between -** 0xd800 and 0xe000 then it is rendered as 0xfffd. -** -** * Bytes in the range of 0x80 through 0xbf which occur as the first -** byte of a character are interpreted as single-byte characters -** and rendered as themselves even though they are technically -** invalid characters. -** -** * This routine accepts an infinite number of different UTF8 encodings -** for unicode values 0x80 and greater. It do not change over-length -** encodings to 0xfffd as some systems recommend. -*/ -#define READ_UTF8(zIn, zTerm, c) \ - c = *(zIn++); \ - if( c>=0xc0 ){ \ - c = UtfTrans1[c-0xc0]; \ - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ - c = (c<<6) + (0x3f & *(zIn++)); \ - } \ - if( c<0x80 \ - || (c&0xFFFFF800)==0xD800 \ - || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ - } -JX9_PRIVATE int jx9Utf8Read( - const unsigned char *z, /* First byte of UTF-8 character */ - const unsigned char *zTerm, /* Pretend this byte is 0x00 */ - const unsigned char **pzNext /* Write first byte past UTF-8 char here */ -){ - int c; - READ_UTF8(z, zTerm, c); - *pzNext = z; - return c; -} -/* - * string utf8_decode(string $data) - * This function decodes data, assumed to be UTF-8 encoded, to unicode. - * Parameters - * data - * An UTF-8 encoded string. - * Return - * Unicode decoded string or NULL on failure. - */ -static int vm_builtin_utf8_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nByte, c; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte); - if( nByte < 1 ){ - /* Empty string, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - zEnd = &zIn[nByte]; - /* Start the decoding process */ - while( zIn < zEnd ){ - c = jx9Utf8Read(zIn, zEnd, &zIn); - if( c == 0x0 ){ - break; - } - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - } - return JX9_OK; -} -/* - * string json_encode(mixed $value) - * Returns a string containing the JSON representation of value. - * Parameters - * $value - * The value being encoded. Can be any type except a resource. - * Return - * Returns a JSON encoded string on success. FALSE otherwise - */ -static int vm_builtin_json_encode(jx9_context *pCtx,int nArg,jx9_value **apArg) -{ - SyBlob sBlob; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Init the working buffer */ - SyBlobInit(&sBlob,&pCtx->pVm->sAllocator); - /* Perform the encoding operation */ - jx9JsonSerialize(apArg[0],&sBlob); - /* Return the serialized value */ - jx9_result_string(pCtx,(const char *)SyBlobData(&sBlob),(int)SyBlobLength(&sBlob)); - /* Cleanup */ - SyBlobRelease(&sBlob); - /* All done */ - return JX9_OK; -} -/* - * mixed json_decode(string $json) - * Takes a JSON encoded string and converts it into a JX9 variable. - * Parameters - * $json - * The json string being decoded. - * Return - * The value encoded in json in appropriate JX9 type. Values true, false and null (case-insensitive) - * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded - * or if the encoded data is deeper than the recursion limit. - */ -static int vm_builtin_json_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zJSON; - int nByte; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the JSON string */ - zJSON = jx9_value_to_string(apArg[0], &nByte); - if( nByte < 1 ){ - /* Empty string, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Decode the raw JSON */ - jx9JsonDecode(pCtx,zJSON,nByte); - return JX9_OK; -} -/* Table of built-in VM functions. */ -static const jx9_builtin_func aVmFunc[] = { - /* JSON Encoding/Decoding */ - { "json_encode", vm_builtin_json_encode }, - { "json_decode", vm_builtin_json_decode }, - /* Functions calls */ - { "func_num_args" , vm_builtin_func_num_args }, - { "func_get_arg" , vm_builtin_func_get_arg }, - { "func_get_args" , vm_builtin_func_get_args }, - { "function_exists", vm_builtin_func_exists }, - { "is_callable" , vm_builtin_is_callable }, - { "get_defined_functions", vm_builtin_get_defined_func }, - /* Constants management */ - { "defined", vm_builtin_defined }, - { "get_defined_constants", vm_builtin_get_defined_constants }, - /* Random numbers/strings generators */ - { "rand", vm_builtin_rand }, - { "rand_str", vm_builtin_rand_str }, - { "getrandmax", vm_builtin_getrandmax }, - /* Language constructs functions */ - { "print", vm_builtin_print }, - { "exit", vm_builtin_exit }, - { "die", vm_builtin_exit }, - /* Variable handling functions */ - { "gettype", vm_builtin_gettype }, - { "get_resource_type", vm_builtin_get_resource_type}, - /* Variable dumping */ - { "dump", vm_builtin_dump }, - /* Release info */ - {"jx9_version", vm_builtin_jx9_version }, - {"jx9_credits", vm_builtin_jx9_version }, - {"jx9_info", vm_builtin_jx9_version }, - {"jx9_copyright", vm_builtin_jx9_version }, - /* hashmap */ - {"extract", vm_builtin_extract }, - /* URL related function */ - {"parse_url", vm_builtin_parse_url }, - /* UTF-8 encoding/decoding */ - {"utf8_encode", vm_builtin_utf8_encode}, - {"utf8_decode", vm_builtin_utf8_decode}, - /* Command line processing */ - {"getopt", vm_builtin_getopt }, - /* Files/URI inclusion facility */ - { "include", vm_builtin_include }, - { "import", vm_builtin_import } -}; -/* - * Register the built-in VM functions defined above. - */ -static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm) -{ - sxi32 rc; - sxu32 n; - for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){ - /* Note that these special functions have access - * to the underlying virtual machine as their - * private data. - */ - rc = jx9_create_function(&(*pVm), aVmFunc[n].zName, aVmFunc[n].xFunc, &(*pVm)); - if( rc != SXRET_OK ){ - return rc; - } - } - return SXRET_OK; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -/* - * Extract the IO stream device associated with a given scheme. - * Return a pointer to an instance of jx9_io_stream when the scheme - * have an associated IO stream registered with it. NULL otherwise. - * If no scheme:// is avalilable then the file:// scheme is assumed. - * For more information on how to register IO stream devices, please - * refer to the official documentation. - */ -JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice( - jx9_vm *pVm, /* Target VM */ - const char **pzDevice, /* Full path, URI, ... */ - int nByte /* *pzDevice length*/ - ) -{ - const char *zIn, *zEnd, *zCur, *zNext; - jx9_io_stream **apStream, *pStream; - SyString sDev, sCur; - sxu32 n, nEntry; - int rc; - /* Check if a scheme [i.e: file://, http://, zip://...] is available */ - zNext = zCur = zIn = *pzDevice; - zEnd = &zIn[nByte]; - while( zIn < zEnd ){ - if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){ - /* Got one */ - zNext = &zIn[sizeof("://")-1]; - break; - } - /* Advance the cursor */ - zIn++; - } - if( zIn >= zEnd ){ - /* No such scheme, return the default stream */ - return pVm->pDefStream; - } - SyStringInitFromBuf(&sDev, zCur, zIn-zCur); - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sDev); - /* Perform a linear lookup on the installed stream devices */ - apStream = (jx9_io_stream **)SySetBasePtr(&pVm->aIOstream); - nEntry = SySetUsed(&pVm->aIOstream); - for( n = 0 ; n < nEntry ; n++ ){ - pStream = apStream[n]; - SyStringInitFromBuf(&sCur, pStream->zName, SyStrlen(pStream->zName)); - /* Perfrom a case-insensitive comparison */ - rc = SyStringCmp(&sDev, &sCur, SyStrnicmp); - if( rc == 0 ){ - /* Stream device found */ - *pzDevice = zNext; - return pStream; - } - } - /* No such stream, return NULL */ - return 0; -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* - * Section: - * HTTP/URI related routines. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ - /* - * URI Parser: Split an URI into components [i.e: Host, Path, Query, ...]. - * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document] - * This almost, but not quite, RFC1738 URI syntax. - * This routine is not a validator, it does not check for validity - * nor decode URI parts, the only thing this routine does is splitting - * the input to its fields. - * Upper layer are responsible of decoding and validating URI parts. - * On success, this function populate the "SyhttpUri" structure passed - * as the first argument. Otherwise SXERR_* is returned when a malformed - * input is encountered. - */ - static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen) - { - const char *zEnd = &zUri[nLen]; - sxu8 bHostOnly = FALSE; - sxu8 bIPv6 = FALSE ; - const char *zCur; - SyString *pComp; - sxu32 nPos = 0; - sxi32 rc; - /* Zero the structure first */ - SyZero(pOut, sizeof(SyhttpUri)); - /* Remove leading and trailing white spaces */ - SyStringInitFromBuf(&pOut->sRaw, zUri, nLen); - SyStringFullTrim(&pOut->sRaw); - /* Find the first '/' separator */ - rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos); - if( rc != SXRET_OK ){ - /* Assume a host name only */ - zCur = zEnd; - bHostOnly = TRUE; - goto ProcessHost; - } - zCur = &zUri[nPos]; - if( zUri != zCur && zCur[-1] == ':' ){ - /* Extract a scheme: - * Not that we can get an invalid scheme here. - * Fortunately the caller can discard any URI by comparing this scheme with its - * registered schemes and will report the error as soon as his comparison function - * fail. - */ - pComp = &pOut->sScheme; - SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri - 1)); - SyStringLeftTrim(pComp); - } - if( zCur[1] != '/' ){ - if( zCur == zUri || zCur[-1] == ':' ){ - /* No authority */ - goto PathSplit; - } - /* There is something here , we will assume its an authority - * and someone has forgot the two prefix slashes "//", - * sooner or later we will detect if we are dealing with a malicious - * user or not, but now assume we are dealing with an authority - * and let the caller handle all the validation process. - */ - goto ProcessHost; - } - zUri = &zCur[2]; - zCur = zEnd; - rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos); - if( rc == SXRET_OK ){ - zCur = &zUri[nPos]; - } - ProcessHost: - /* Extract user information if present */ - rc = SyByteFind(zUri, (sxu32)(zCur - zUri), '@', &nPos); - if( rc == SXRET_OK ){ - if( nPos > 0 ){ - sxu32 nPassOfft; /* Password offset */ - pComp = &pOut->sUser; - SyStringInitFromBuf(pComp, zUri, nPos); - /* Extract the password if available */ - rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPassOfft); - if( rc == SXRET_OK && nPassOfft < nPos){ - pComp->nByte = nPassOfft; - pComp = &pOut->sPass; - pComp->zString = &zUri[nPassOfft+sizeof(char)]; - pComp->nByte = nPos - nPassOfft - 1; - } - /* Update the cursor */ - zUri = &zUri[nPos+1]; - }else{ - zUri++; - } - } - pComp = &pOut->sHost; - while( zUri < zCur && SyisSpace(zUri[0])){ - zUri++; - } - SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri)); - if( pComp->zString[0] == '[' ){ - /* An IPv6 Address: Make a simple naive test - */ - zUri++; pComp->zString++; pComp->nByte = 0; - while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){ - zUri++; pComp->nByte++; - } - if( zUri[0] != ']' ){ - return SXERR_CORRUPT; /* Malformed IPv6 address */ - } - zUri++; - bIPv6 = TRUE; - } - /* Extract a port number if available */ - rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPos); - if( rc == SXRET_OK ){ - if( bIPv6 == FALSE ){ - pComp->nByte = (sxu32)(&zUri[nPos] - zUri); - } - pComp = &pOut->sPort; - SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zCur - &zUri[nPos+1])); - } - if( bHostOnly == TRUE ){ - return SXRET_OK; - } -PathSplit: - zUri = zCur; - pComp = &pOut->sPath; - SyStringInitFromBuf(pComp, zUri, (sxu32)(zEnd-zUri)); - if( pComp->nByte == 0 ){ - return SXRET_OK; /* Empty path */ - } - if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '?', &nPos) ){ - pComp->nByte = nPos; /* Update path length */ - pComp = &pOut->sQuery; - SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1])); - } - if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '#', &nPos) ){ - /* Update path or query length */ - if( pComp == &pOut->sPath ){ - pComp->nByte = nPos; - }else{ - if( &zUri[nPos] < (char *)SyStringData(pComp) ){ - /* Malformed syntax : Query must be present before fragment */ - return SXERR_SYNTAX; - } - pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]); - } - pComp = &pOut->sFragment; - SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1])) - } - return SXRET_OK; - } - /* - * Extract a single line from a raw HTTP request. - * Return SXRET_OK on success, SXERR_EOF when end of input - * and SXERR_MORE when more input is needed. - */ -static sxi32 VmGetNextLine(SyString *pCursor, SyString *pCurrent) -{ - const char *zIn; - sxu32 nPos; - /* Jump leading white spaces */ - SyStringLeftTrim(pCursor); - if( pCursor->nByte < 1 ){ - SyStringInitFromBuf(pCurrent, 0, 0); - return SXERR_EOF; /* End of input */ - } - zIn = SyStringData(pCursor); - if( SXRET_OK != SyByteListFind(pCursor->zString, pCursor->nByte, "\r\n", &nPos) ){ - /* Line not found, tell the caller to read more input from source */ - SyStringDupPtr(pCurrent, pCursor); - return SXERR_MORE; - } - pCurrent->zString = zIn; - pCurrent->nByte = nPos; - /* advance the cursor so we can call this routine again */ - pCursor->zString = &zIn[nPos]; - pCursor->nByte -= nPos; - return SXRET_OK; - } - /* - * Split a single MIME header into a name value pair. - * This function return SXRET_OK, SXERR_CONTINUE on success. - * Otherwise SXERR_NEXT is returned when a malformed header - * is encountered. - * Note: This function handle also mult-line headers. - */ - static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr, SyhttpHeader *pLast, const char *zLine, sxu32 nLen) - { - SyString *pName; - sxu32 nPos; - sxi32 rc; - if( nLen < 1 ){ - return SXERR_NEXT; - } - /* Check for multi-line header */ - if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){ - SyString *pTmp = &pLast->sValue; - SyStringFullTrim(pTmp); - if( pTmp->nByte == 0 ){ - SyStringInitFromBuf(pTmp, zLine, nLen); - }else{ - /* Update header value length */ - pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString); - } - /* Simply tell the caller to reset its states and get another line */ - return SXERR_CONTINUE; - } - /* Split the header */ - pName = &pHdr->sName; - rc = SyByteFind(zLine, nLen, ':', &nPos); - if(rc != SXRET_OK ){ - return SXERR_NEXT; /* Malformed header;Check the next entry */ - } - SyStringInitFromBuf(pName, zLine, nPos); - SyStringFullTrim(pName); - /* Extract a header value */ - SyStringInitFromBuf(&pHdr->sValue, &zLine[nPos + 1], nLen - nPos - 1); - /* Remove leading and trailing whitespaces */ - SyStringFullTrim(&pHdr->sValue); - return SXRET_OK; - } - /* - * Extract all MIME headers associated with a HTTP request. - * After processing the first line of a HTTP request, the following - * routine is called in order to extract MIME headers. - * This function return SXRET_OK on success, SXERR_MORE when it needs - * more inputs. - * Note: Any malformed header is simply discarded. - */ - static sxi32 VmHttpExtractHeaders(SyString *pRequest, SySet *pOut) - { - SyhttpHeader *pLast = 0; - SyString sCurrent; - SyhttpHeader sHdr; - sxu8 bEol; - sxi32 rc; - if( SySetUsed(pOut) > 0 ){ - pLast = (SyhttpHeader *)SySetAt(pOut, SySetUsed(pOut)-1); - } - bEol = FALSE; - for(;;){ - SyZero(&sHdr, sizeof(SyhttpHeader)); - /* Extract a single line from the raw HTTP request */ - rc = VmGetNextLine(pRequest, &sCurrent); - if(rc != SXRET_OK ){ - if( sCurrent.nByte < 1 ){ - break; - } - bEol = TRUE; - } - /* Process the header */ - if( SXRET_OK == VmHttpProcessOneHeader(&sHdr, pLast, sCurrent.zString, sCurrent.nByte)){ - if( SXRET_OK != SySetPut(pOut, (const void *)&sHdr) ){ - break; - } - /* Retrieve the last parsed header so we can handle multi-line header - * in case we face one of them. - */ - pLast = (SyhttpHeader *)SySetPeek(pOut); - } - if( bEol ){ - break; - } - } /* for(;;) */ - return SXRET_OK; - } - /* - * Process the first line of a HTTP request. - * This routine perform the following operations - * 1) Extract the HTTP method. - * 2) Split the request URI to it's fields [ie: host, path, query, ...]. - * 3) Extract the HTTP protocol version. - */ - static sxi32 VmHttpProcessFirstLine( - SyString *pRequest, /* Raw HTTP request */ - sxi32 *pMethod, /* OUT: HTTP method */ - SyhttpUri *pUri, /* OUT: Parse of the URI */ - sxi32 *pProto /* OUT: HTTP protocol */ - ) - { - static const char *azMethods[] = { "get", "post", "head", "put"}; - static const sxi32 aMethods[] = { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD, HTTP_METHOD_PUT}; - const char *zIn, *zEnd, *zPtr; - SyString sLine; - sxu32 nLen; - sxi32 rc; - /* Extract the first line and update the pointer */ - rc = VmGetNextLine(pRequest, &sLine); - if( rc != SXRET_OK ){ - return rc; - } - if ( sLine.nByte < 1 ){ - /* Empty HTTP request */ - return SXERR_EMPTY; - } - /* Delimit the line and ignore trailing and leading white spaces */ - zIn = sLine.zString; - zEnd = &zIn[sLine.nByte]; - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ - zIn++; - } - /* Extract the HTTP method */ - zPtr = zIn; - while( zIn < zEnd && !SyisSpace(zIn[0]) ){ - zIn++; - } - *pMethod = HTTP_METHOD_OTHR; - if( zIn > zPtr ){ - sxu32 i; - nLen = (sxu32)(zIn-zPtr); - for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){ - if( SyStrnicmp(azMethods[i], zPtr, nLen) == 0 ){ - *pMethod = aMethods[i]; - break; - } - } - } - /* Jump trailing white spaces */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ - zIn++; - } - /* Extract the request URI */ - zPtr = zIn; - while( zIn < zEnd && !SyisSpace(zIn[0]) ){ - zIn++; - } - if( zIn > zPtr ){ - nLen = (sxu32)(zIn-zPtr); - /* Split raw URI to it's fields */ - VmHttpSplitURI(pUri, zPtr, nLen); - } - /* Jump trailing white spaces */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ - zIn++; - } - /* Extract the HTTP version */ - zPtr = zIn; - while( zIn < zEnd && !SyisSpace(zIn[0]) ){ - zIn++; - } - *pProto = HTTP_PROTO_11; /* HTTP/1.1 */ - rc = 1; - if( zIn > zPtr ){ - rc = SyStrnicmp(zPtr, "http/1.0", (sxu32)(zIn-zPtr)); - } - if( !rc ){ - *pProto = HTTP_PROTO_10; /* HTTP/1.0 */ - } - return SXRET_OK; - } - /* - * Tokenize, decode and split a raw query encoded as: "x-www-form-urlencoded" - * into a name value pair. - * Note that this encoding is implicit in GET based requests. - * After the tokenization process, register the decoded queries - * in the $_GET/$_POST/$_REQUEST superglobals arrays. - */ - static sxi32 VmHttpSplitEncodedQuery( - jx9_vm *pVm, /* Target VM */ - SyString *pQuery, /* Raw query to decode */ - SyBlob *pWorker, /* Working buffer */ - int is_post /* TRUE if we are dealing with a POST request */ - ) - { - const char *zEnd = &pQuery->zString[pQuery->nByte]; - const char *zIn = pQuery->zString; - jx9_value *pGet, *pRequest; - SyString sName, sValue; - const char *zPtr; - sxu32 nBlobOfft; - /* Extract superglobals */ - if( is_post ){ - /* $_POST superglobal */ - pGet = VmExtractSuper(&(*pVm), "_POST", sizeof("_POST")-1); - }else{ - /* $_GET superglobal */ - pGet = VmExtractSuper(&(*pVm), "_GET", sizeof("_GET")-1); - } - pRequest = VmExtractSuper(&(*pVm), "_REQUEST", sizeof("_REQUEST")-1); - /* Split up the raw query */ - for(;;){ - /* Jump leading white spaces */ - while(zIn < zEnd && SyisSpace(zIn[0]) ){ - zIn++; - } - if( zIn >= zEnd ){ - break; - } - zPtr = zIn; - while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){ - zPtr++; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - /* Decode the entry */ - SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE); - /* Save the entry */ - sName.nByte = SyBlobLength(pWorker); - sValue.zString = 0; - sValue.nByte = 0; - if( zPtr < zEnd && zPtr[0] == '=' ){ - zPtr++; - zIn = zPtr; - /* Store field value */ - while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){ - zPtr++; - } - if( zPtr > zIn ){ - /* Decode the value */ - nBlobOfft = SyBlobLength(pWorker); - SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE); - sValue.zString = (const char *)SyBlobDataAt(pWorker, nBlobOfft); - sValue.nByte = SyBlobLength(pWorker) - nBlobOfft; - - } - /* Synchronize pointers */ - zIn = zPtr; - } - sName.zString = (const char *)SyBlobData(pWorker); - /* Install the decoded query in the $_GET/$_REQUEST array */ - if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){ - VmHashmapInsert((jx9_hashmap *)pGet->x.pOther, - sName.zString, (int)sName.nByte, - sValue.zString, (int)sValue.nByte - ); - } - if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){ - VmHashmapInsert((jx9_hashmap *)pRequest->x.pOther, - sName.zString, (int)sName.nByte, - sValue.zString, (int)sValue.nByte - ); - } - /* Advance the pointer */ - zIn = &zPtr[1]; - } - /* All done*/ - return SXRET_OK; - } - /* - * Extract MIME header value from the given set. - * Return header value on success. NULL otherwise. - */ - static SyString * VmHttpExtractHeaderValue(SySet *pSet, const char *zMime, sxu32 nByte) - { - SyhttpHeader *aMime, *pMime; - SyString sMime; - sxu32 n; - SyStringInitFromBuf(&sMime, zMime, nByte); - /* Point to the MIME entries */ - aMime = (SyhttpHeader *)SySetBasePtr(pSet); - /* Perform the lookup */ - for( n = 0 ; n < SySetUsed(pSet) ; ++n ){ - pMime = &aMime[n]; - if( SyStringCmp(&sMime, &pMime->sName, SyStrnicmp) == 0 ){ - /* Header found, return it's associated value */ - return &pMime->sValue; - } - } - /* No such MIME header */ - return 0; - } - /* - * Tokenize and decode a raw "Cookie:" MIME header into a name value pair - * and insert it's fields [i.e name, value] in the $_COOKIE superglobal. - */ - static sxi32 VmHttpPorcessCookie(jx9_vm *pVm, SyBlob *pWorker, const char *zIn, sxu32 nByte) - { - const char *zPtr, *zDelimiter, *zEnd = &zIn[nByte]; - SyString sName, sValue; - jx9_value *pCookie; - sxu32 nOfft; - /* Make sure the $_COOKIE superglobal is available */ - pCookie = VmExtractSuper(&(*pVm), "_COOKIE", sizeof("_COOKIE")-1); - if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* $_COOKIE superglobal not available */ - return SXERR_NOTFOUND; - } - for(;;){ - /* Jump leading white spaces */ - while( zIn < zEnd && SyisSpace(zIn[0]) ){ - zIn++; - } - if( zIn >= zEnd ){ - break; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - zDelimiter = zIn; - /* Delimit the name[=value]; pair */ - while( zDelimiter < zEnd && zDelimiter[0] != ';' ){ - zDelimiter++; - } - zPtr = zIn; - while( zPtr < zDelimiter && zPtr[0] != '=' ){ - zPtr++; - } - /* Decode the cookie */ - SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE); - sName.nByte = SyBlobLength(pWorker); - zPtr++; - sValue.zString = 0; - sValue.nByte = 0; - if( zPtr < zDelimiter ){ - /* Got a Cookie value */ - nOfft = SyBlobLength(pWorker); - SyUriDecode(zPtr, (sxu32)(zDelimiter-zPtr), jx9VmBlobConsumer, pWorker, TRUE); - SyStringInitFromBuf(&sValue, SyBlobDataAt(pWorker, nOfft), SyBlobLength(pWorker)-nOfft); - } - /* Synchronize pointers */ - zIn = &zDelimiter[1]; - /* Perform the insertion */ - sName.zString = (const char *)SyBlobData(pWorker); - VmHashmapInsert((jx9_hashmap *)pCookie->x.pOther, - sName.zString, (int)sName.nByte, - sValue.zString, (int)sValue.nByte - ); - } - return SXRET_OK; - } - /* - * Process a full HTTP request and populate the appropriate arrays - * such as $_SERVER, $_GET, $_POST, $_COOKIE, $_REQUEST, ... with the information - * extracted from the raw HTTP request. As an extension Symisc introduced - * the $_HEADER array which hold a copy of the processed HTTP MIME headers - * and their associated values. [i.e: $_HEADER['Server'], $_HEADER['User-Agent'], ...]. - * This function return SXRET_OK on success. Any other return value indicates - * a malformed HTTP request. - */ - static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte) - { - SyString *pName, *pValue, sRequest; /* Raw HTTP request */ - jx9_value *pHeaderArray; /* $_HEADER superglobal (Symisc eXtension to the JX9 specification)*/ - SyhttpHeader *pHeader; /* MIME header */ - SyhttpUri sUri; /* Parse of the raw URI*/ - SyBlob sWorker; /* General purpose working buffer */ - SySet sHeader; /* MIME headers set */ - sxi32 iMethod; /* HTTP method [i.e: GET, POST, HEAD...]*/ - sxi32 iVer; /* HTTP protocol version */ - sxi32 rc; - SyStringInitFromBuf(&sRequest, zRequest, nByte); - SySetInit(&sHeader, &pVm->sAllocator, sizeof(SyhttpHeader)); - SyBlobInit(&sWorker, &pVm->sAllocator); - /* Ignore leading and trailing white spaces*/ - SyStringFullTrim(&sRequest); - /* Process the first line */ - rc = VmHttpProcessFirstLine(&sRequest, &iMethod, &sUri, &iVer); - if( rc != SXRET_OK ){ - return rc; - } - /* Process MIME headers */ - VmHttpExtractHeaders(&sRequest, &sHeader); - /* - * Setup $_SERVER environments - */ - /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "SERVER_PROTOCOL", - iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1", - sizeof("HTTP/1.1")-1 - ); - /* 'REQUEST_METHOD': Which request method was used to access the page */ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "REQUEST_METHOD", - iMethod == HTTP_METHOD_GET ? "GET" : - (iMethod == HTTP_METHOD_POST ? "POST": - (iMethod == HTTP_METHOD_PUT ? "PUT" : - (iMethod == HTTP_METHOD_HEAD ? "HEAD" : "OTHER"))), - -1 /* Compute attribute length automatically */ - ); - if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){ - pValue = &sUri.sQuery; - /* 'QUERY_STRING': The query string, if any, via which the page was accessed */ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "QUERY_STRING", - pValue->zString, - pValue->nByte - ); - /* Decoded the raw query */ - VmHttpSplitEncodedQuery(&(*pVm), pValue, &sWorker, FALSE); - } - /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */ - pValue = &sUri.sRaw; - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "REQUEST_URI", - pValue->zString, - pValue->nByte - ); - /* - * 'PATH_INFO' - * 'ORIG_PATH_INFO' - * Contains any client-provided pathname information trailing the actual script filename but preceding - * the query string, if available. For instance, if the current script was accessed via the URL - * http://www.example.com/jx9/path_info.jx9/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain - * /some/stuff. - */ - pValue = &sUri.sPath; - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "PATH_INFO", - pValue->zString, - pValue->nByte - ); - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "ORIG_PATH_INFO", - pValue->zString, - pValue->nByte - ); - /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Accept", sizeof("Accept")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_ACCEPT", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Charset", sizeof("Accept-Charset")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_ACCEPT_CHARSET", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Encoding", sizeof("Accept-Encoding")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_ACCEPT_ENCODING", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Language", sizeof("Accept-Language")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_ACCEPT_LANGUAGE", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Connection", sizeof("Connection")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_CONNECTION", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Host", sizeof("Host")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_HOST", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Referer", sizeof("Referer")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_REFERER", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "User-Agent", sizeof("User-Agent")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_USER_AGENT", - pValue->zString, - pValue->nByte - ); - } - /* 'JX9_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization' - * header sent by the client (which you should then use to make the appropriate validation). - */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Authorization", sizeof("Authorization")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "JX9_AUTH_DIGEST", - pValue->zString, - pValue->nByte - ); - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "JX9_AUTH", - pValue->zString, - pValue->nByte - ); - } - /* Install all clients HTTP headers in the $_HEADER superglobal */ - pHeaderArray = VmExtractSuper(&(*pVm), "_HEADER", sizeof("_HEADER")-1); - /* Iterate throw the available MIME headers*/ - SySetResetCursor(&sHeader); - pHeader = 0; /* stupid cc warning */ - while( SXRET_OK == SySetGetNextEntry(&sHeader, (void **)&pHeader) ){ - pName = &pHeader->sName; - pValue = &pHeader->sValue; - if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){ - /* Insert the MIME header and it's associated value */ - VmHashmapInsert((jx9_hashmap *)pHeaderArray->x.pOther, - pName->zString, (int)pName->nByte, - pValue->zString, (int)pValue->nByte - ); - } - if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString, "Cookie", sizeof("Cookie")-1) == 0 - && pValue->nByte > 0){ - /* Process the name=value pair and insert them in the $_COOKIE superglobal array */ - VmHttpPorcessCookie(&(*pVm), &sWorker, pValue->zString, pValue->nByte); - } - } - if( iMethod == HTTP_METHOD_POST ){ - /* Extract raw POST data */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Type", sizeof("Content-Type") - 1); - if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 && - SyMemcmp("application/x-www-form-urlencoded", pValue->zString, pValue->nByte) == 0 ){ - /* Extract POST data length */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Length", sizeof("Content-Length") - 1); - if( pValue ){ - sxi32 iLen = 0; /* POST data length */ - SyStrToInt32(pValue->zString, pValue->nByte, (void *)&iLen, 0); - if( iLen > 0 ){ - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sRequest); - if( (int)sRequest.nByte > iLen ){ - sRequest.nByte = (sxu32)iLen; - } - /* Decode POST data now */ - VmHttpSplitEncodedQuery(&(*pVm), &sRequest, &sWorker, TRUE); - } - } - } - } - /* All done, clean-up the mess left behind */ - SySetRelease(&sHeader); - SyBlobRelease(&sWorker); - return SXRET_OK; - } -- cgit v1.2.3