summaryrefslogtreecommitdiffstats
path: root/common/unqlite/jx9_vm.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/unqlite/jx9_vm.c')
-rw-r--r--common/unqlite/jx9_vm.c7141
1 files changed, 7141 insertions, 0 deletions
diff --git a/common/unqlite/jx9_vm.c b/common/unqlite/jx9_vm.c
new file mode 100644
index 0000000..1585b13
--- /dev/null
+++ b/common/unqlite/jx9_vm.c
@@ -0,0 +1,7141 @@
1/*
2 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
3 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
4 * Version 1.7.2
5 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6 * please contact Symisc Systems via:
7 * legal@symisc.net
8 * licensing@symisc.net
9 * contact@symisc.net
10 * or visit:
11 * http://jx9.symisc.net/
12 */
13 /* $SymiscID: jx9_vm.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
14#ifndef JX9_AMALGAMATION
15#include "jx9Int.h"
16#endif
17/*
18 * The code in this file implements execution method of the JX9 Virtual Machine.
19 * The JX9 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program
20 * which is then executed by the virtual machine implemented here to do the work of the JX9
21 * statements.
22 * JX9 bytecode programs are similar in form to assembly language. The program consists
23 * of a linear sequence of operations .Each operation has an opcode and 3 operands.
24 * Operands P1 and P2 are integers where the first is signed while the second is unsigned.
25 * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually
26 * the jump destination used by the OP_JMP, OP_JZ, OP_JNZ, ... instructions.
27 * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands.
28 * Computation results are stored on a stack. Each entry on the stack is of type jx9_value.
29 * JX9 uses the jx9_value object to represent all values that can be stored in a JX9 variable.
30 * Since JX9 uses dynamic typing for the values it stores. Values stored in jx9_value objects
31 * can be integers, floating point values, strings, arrays, object instances (object in the JX9 jargon)
32 * and so on.
33 * Internally, the JX9 virtual machine manipulates nearly all values as jx9_values structures.
34 * Each jx9_value may cache multiple representations(string, integer etc.) of the same value.
35 * An implicit conversion from one type to the other occurs as necessary.
36 * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does
37 * the work of interpreting a JX9 bytecode program. But other routines are also provided
38 * to help in building up a program instruction by instruction.
39 */
40/*
41 * Each active virtual machine frame is represented by an instance
42 * of the following structure.
43 * VM Frame hold local variables and other stuff related to function call.
44 */
45struct VmFrame
46{
47 VmFrame *pParent; /* Parent frame or NULL if global scope */
48 void *pUserData; /* Upper layer private data associated with this frame */
49 SySet sLocal; /* Local variables container (VmSlot instance) */
50 jx9_vm *pVm; /* VM that own this frame */
51 SyHash hVar; /* Variable hashtable for fast lookup */
52 SySet sArg; /* Function arguments container */
53 sxi32 iFlags; /* Frame configuration flags (See below)*/
54 sxu32 iExceptionJump; /* Exception jump destination */
55};
56/*
57 * When a user defined variable is garbage collected, memory object index
58 * is stored in an instance of the following structure and put in the free object
59 * table so that it can be reused again without allocating a new memory object.
60 */
61typedef struct VmSlot VmSlot;
62struct VmSlot
63{
64 sxu32 nIdx; /* Index in pVm->aMemObj[] */
65 void *pUserData; /* Upper-layer private data */
66};
67/*
68 * Each parsed URI is recorded and stored in an instance of the following structure.
69 * This structure and it's related routines are taken verbatim from the xHT project
70 * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
71 * the xHT project is developed internally by Symisc Systems.
72 */
73typedef struct SyhttpUri SyhttpUri;
74struct SyhttpUri
75{
76 SyString sHost; /* Hostname or IP address */
77 SyString sPort; /* Port number */
78 SyString sPath; /* Mandatory resource path passed verbatim (Not decoded) */
79 SyString sQuery; /* Query part */
80 SyString sFragment; /* Fragment part */
81 SyString sScheme; /* Scheme */
82 SyString sUser; /* Username */
83 SyString sPass; /* Password */
84 SyString sRaw; /* Raw URI */
85};
86/*
87 * An instance of the following structure is used to record all MIME headers seen
88 * during a HTTP interaction.
89 * This structure and it's related routines are taken verbatim from the xHT project
90 * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
91 * the xHT project is developed internally by Symisc Systems.
92 */
93typedef struct SyhttpHeader SyhttpHeader;
94struct SyhttpHeader
95{
96 SyString sName; /* Header name [i.e:"Content-Type", "Host", "User-Agent"]. NOT NUL TERMINATED */
97 SyString sValue; /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */
98};
99/*
100 * Supported HTTP methods.
101 */
102#define HTTP_METHOD_GET 1 /* GET */
103#define HTTP_METHOD_HEAD 2 /* HEAD */
104#define HTTP_METHOD_POST 3 /* POST */
105#define HTTP_METHOD_PUT 4 /* PUT */
106#define HTTP_METHOD_OTHR 5 /* Other HTTP methods [i.e: DELETE, TRACE, OPTIONS...]*/
107/*
108 * Supported HTTP protocol version.
109 */
110#define HTTP_PROTO_10 1 /* HTTP/1.0 */
111#define HTTP_PROTO_11 2 /* HTTP/1.1 */
112/*
113 * Register a constant and it's associated expansion callback so that
114 * it can be expanded from the target JX9 program.
115 * The constant expansion mechanism under JX9 is extremely powerful yet
116 * simple and work as follows:
117 * Each registered constant have a C procedure associated with it.
118 * This procedure known as the constant expansion callback is responsible
119 * of expanding the invoked constant to the desired value, for example:
120 * The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI).
121 * The "__OS__" constant procedure expands to the name of the host Operating Systems
122 * (Windows, Linux, ...) and so on.
123 * Please refer to the official documentation for additional information.
124 */
125JX9_PRIVATE sxi32 jx9VmRegisterConstant(
126 jx9_vm *pVm, /* Target VM */
127 const SyString *pName, /* Constant name */
128 ProcConstant xExpand, /* Constant expansion callback */
129 void *pUserData /* Last argument to xExpand() */
130 )
131{
132 jx9_constant *pCons;
133 SyHashEntry *pEntry;
134 char *zDupName;
135 sxi32 rc;
136 pEntry = SyHashGet(&pVm->hConstant, (const void *)pName->zString, pName->nByte);
137 if( pEntry ){
138 /* Overwrite the old definition and return immediately */
139 pCons = (jx9_constant *)pEntry->pUserData;
140 pCons->xExpand = xExpand;
141 pCons->pUserData = pUserData;
142 return SXRET_OK;
143 }
144 /* Allocate a new constant instance */
145 pCons = (jx9_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_constant));
146 if( pCons == 0 ){
147 return 0;
148 }
149 /* Duplicate constant name */
150 zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
151 if( zDupName == 0 ){
152 SyMemBackendPoolFree(&pVm->sAllocator, pCons);
153 return 0;
154 }
155 /* Install the constant */
156 SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte);
157 pCons->xExpand = xExpand;
158 pCons->pUserData = pUserData;
159 rc = SyHashInsert(&pVm->hConstant, (const void *)zDupName, SyStringLength(&pCons->sName), pCons);
160 if( rc != SXRET_OK ){
161 SyMemBackendFree(&pVm->sAllocator, zDupName);
162 SyMemBackendPoolFree(&pVm->sAllocator, pCons);
163 return rc;
164 }
165 /* All done, constant can be invoked from JX9 code */
166 return SXRET_OK;
167}
168/*
169 * Allocate a new foreign function instance.
170 * This function return SXRET_OK on success. Any other
171 * return value indicates failure.
172 * Please refer to the official documentation for an introduction to
173 * the foreign function mechanism.
174 */
175static sxi32 jx9NewForeignFunction(
176 jx9_vm *pVm, /* Target VM */
177 const SyString *pName, /* Foreign function name */
178 ProcHostFunction xFunc, /* Foreign function implementation */
179 void *pUserData, /* Foreign function private data */
180 jx9_user_func **ppOut /* OUT: VM image of the foreign function */
181 )
182{
183 jx9_user_func *pFunc;
184 char *zDup;
185 /* Allocate a new user function */
186 pFunc = (jx9_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_user_func));
187 if( pFunc == 0 ){
188 return SXERR_MEM;
189 }
190 /* Duplicate function name */
191 zDup = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
192 if( zDup == 0 ){
193 SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
194 return SXERR_MEM;
195 }
196 /* Zero the structure */
197 SyZero(pFunc, sizeof(jx9_user_func));
198 /* Initialize structure fields */
199 SyStringInitFromBuf(&pFunc->sName, zDup, pName->nByte);
200 pFunc->pVm = pVm;
201 pFunc->xFunc = xFunc;
202 pFunc->pUserData = pUserData;
203 SySetInit(&pFunc->aAux, &pVm->sAllocator, sizeof(jx9_aux_data));
204 /* Write a pointer to the new function */
205 *ppOut = pFunc;
206 return SXRET_OK;
207}
208/*
209 * Install a foreign function and it's associated callback so that
210 * it can be invoked from the target JX9 code.
211 * This function return SXRET_OK on successful registration. Any other
212 * return value indicates failure.
213 * Please refer to the official documentation for an introduction to
214 * the foreign function mechanism.
215 */
216JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(
217 jx9_vm *pVm, /* Target VM */
218 const SyString *pName, /* Foreign function name */
219 ProcHostFunction xFunc, /* Foreign function implementation */
220 void *pUserData /* Foreign function private data */
221 )
222{
223 jx9_user_func *pFunc;
224 SyHashEntry *pEntry;
225 sxi32 rc;
226 /* Overwrite any previously registered function with the same name */
227 pEntry = SyHashGet(&pVm->hHostFunction, pName->zString, pName->nByte);
228 if( pEntry ){
229 pFunc = (jx9_user_func *)pEntry->pUserData;
230 pFunc->pUserData = pUserData;
231 pFunc->xFunc = xFunc;
232 SySetReset(&pFunc->aAux);
233 return SXRET_OK;
234 }
235 /* Create a new user function */
236 rc = jx9NewForeignFunction(&(*pVm), &(*pName), xFunc, pUserData, &pFunc);
237 if( rc != SXRET_OK ){
238 return rc;
239 }
240 /* Install the function in the corresponding hashtable */
241 rc = SyHashInsert(&pVm->hHostFunction, SyStringData(&pFunc->sName), pName->nByte, pFunc);
242 if( rc != SXRET_OK ){
243 SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
244 SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
245 return rc;
246 }
247 /* User function successfully installed */
248 return SXRET_OK;
249}
250/*
251 * Initialize a VM function.
252 */
253JX9_PRIVATE sxi32 jx9VmInitFuncState(
254 jx9_vm *pVm, /* Target VM */
255 jx9_vm_func *pFunc, /* Target Fucntion */
256 const char *zName, /* Function name */
257 sxu32 nByte, /* zName length */
258 sxi32 iFlags, /* Configuration flags */
259 void *pUserData /* Function private data */
260 )
261{
262 /* Zero the structure */
263 SyZero(pFunc, sizeof(jx9_vm_func));
264 /* Initialize structure fields */
265 /* Arguments container */
266 SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(jx9_vm_func_arg));
267 /* Static variable container */
268 SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(jx9_vm_func_static_var));
269 /* Bytecode container */
270 SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
271 /* Preallocate some instruction slots */
272 SySetAlloc(&pFunc->aByteCode, 0x10);
273 pFunc->iFlags = iFlags;
274 pFunc->pUserData = pUserData;
275 SyStringInitFromBuf(&pFunc->sName, zName, nByte);
276 return SXRET_OK;
277}
278/*
279 * Install a user defined function in the corresponding VM container.
280 */
281JX9_PRIVATE sxi32 jx9VmInstallUserFunction(
282 jx9_vm *pVm, /* Target VM */
283 jx9_vm_func *pFunc, /* Target function */
284 SyString *pName /* Function name */
285 )
286{
287 SyHashEntry *pEntry;
288 sxi32 rc;
289 if( pName == 0 ){
290 /* Use the built-in name */
291 pName = &pFunc->sName;
292 }
293 /* Check for duplicates (functions with the same name) first */
294 pEntry = SyHashGet(&pVm->hFunction, pName->zString, pName->nByte);
295 if( pEntry ){
296 jx9_vm_func *pLink = (jx9_vm_func *)pEntry->pUserData;
297 if( pLink != pFunc ){
298 /* Link */
299 pFunc->pNextName = pLink;
300 pEntry->pUserData = pFunc;
301 }
302 return SXRET_OK;
303 }
304 /* First time seen */
305 pFunc->pNextName = 0;
306 rc = SyHashInsert(&pVm->hFunction, pName->zString, pName->nByte, pFunc);
307 return rc;
308}
309/*
310 * Instruction builder interface.
311 */
312JX9_PRIVATE sxi32 jx9VmEmitInstr(
313 jx9_vm *pVm, /* Target VM */
314 sxi32 iOp, /* Operation to perform */
315 sxi32 iP1, /* First operand */
316 sxu32 iP2, /* Second operand */
317 void *p3, /* Third operand */
318 sxu32 *pIndex /* Instruction index. NULL otherwise */
319 )
320{
321 VmInstr sInstr;
322 sxi32 rc;
323 /* Fill the VM instruction */
324 sInstr.iOp = (sxu8)iOp;
325 sInstr.iP1 = iP1;
326 sInstr.iP2 = iP2;
327 sInstr.p3 = p3;
328 if( pIndex ){
329 /* Instruction index in the bytecode array */
330 *pIndex = SySetUsed(pVm->pByteContainer);
331 }
332 /* Finally, record the instruction */
333 rc = SySetPut(pVm->pByteContainer, (const void *)&sInstr);
334 if( rc != SXRET_OK ){
335 jx9GenCompileError(&pVm->sCodeGen, E_ERROR, 1, "Fatal, Cannot emit instruction due to a memory failure");
336 /* Fall throw */
337 }
338 return rc;
339}
340/*
341 * Swap the current bytecode container with the given one.
342 */
343JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer)
344{
345 if( pContainer == 0 ){
346 /* Point to the default container */
347 pVm->pByteContainer = &pVm->aByteCode;
348 }else{
349 /* Change container */
350 pVm->pByteContainer = &(*pContainer);
351 }
352 return SXRET_OK;
353}
354/*
355 * Return the current bytecode container.
356 */
357JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm)
358{
359 return pVm->pByteContainer;
360}
361/*
362 * Extract the VM instruction rooted at nIndex.
363 */
364JX9_PRIVATE VmInstr * jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex)
365{
366 VmInstr *pInstr;
367 pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex);
368 return pInstr;
369}
370/*
371 * Return the total number of VM instructions recorded so far.
372 */
373JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm)
374{
375 return SySetUsed(pVm->pByteContainer);
376}
377/*
378 * Pop the last VM instruction.
379 */
380JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm)
381{
382 return (VmInstr *)SySetPop(pVm->pByteContainer);
383}
384/*
385 * Peek the last VM instruction.
386 */
387JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm)
388{
389 return (VmInstr *)SySetPeek(pVm->pByteContainer);
390}
391/*
392 * Allocate a new virtual machine frame.
393 */
394static VmFrame * VmNewFrame(
395 jx9_vm *pVm, /* Target VM */
396 void *pUserData /* Upper-layer private data */
397 )
398{
399 VmFrame *pFrame;
400 /* Allocate a new vm frame */
401 pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmFrame));
402 if( pFrame == 0 ){
403 return 0;
404 }
405 /* Zero the structure */
406 SyZero(pFrame, sizeof(VmFrame));
407 /* Initialize frame fields */
408 pFrame->pUserData = pUserData;
409 pFrame->pVm = pVm;
410 SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0);
411 SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot));
412 SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot));
413 return pFrame;
414}
415/*
416 * Enter a VM frame.
417 */
418static sxi32 VmEnterFrame(
419 jx9_vm *pVm, /* Target VM */
420 void *pUserData, /* Upper-layer private data */
421 VmFrame **ppFrame /* OUT: Top most active frame */
422 )
423{
424 VmFrame *pFrame;
425 /* Allocate a new frame */
426 pFrame = VmNewFrame(&(*pVm), pUserData);
427 if( pFrame == 0 ){
428 return SXERR_MEM;
429 }
430 /* Link to the list of active VM frame */
431 pFrame->pParent = pVm->pFrame;
432 pVm->pFrame = pFrame;
433 if( ppFrame ){
434 /* Write a pointer to the new VM frame */
435 *ppFrame = pFrame;
436 }
437 return SXRET_OK;
438}
439/*
440 * Link a foreign variable with the TOP most active frame.
441 * Refer to the JX9_OP_UPLINK instruction implementation for more
442 * information.
443 */
444static sxi32 VmFrameLink(jx9_vm *pVm,SyString *pName)
445{
446 VmFrame *pTarget, *pFrame;
447 SyHashEntry *pEntry = 0;
448 sxi32 rc;
449 /* Point to the upper frame */
450 pFrame = pVm->pFrame;
451 pTarget = pFrame;
452 pFrame = pTarget->pParent;
453 while( pFrame ){
454 /* Query the current frame */
455 pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
456 if( pEntry ){
457 /* Variable found */
458 break;
459 }
460 /* Point to the upper frame */
461 pFrame = pFrame->pParent;
462 }
463 if( pEntry == 0 ){
464 /* Inexistant variable */
465 return SXERR_NOTFOUND;
466 }
467 /* Link to the current frame */
468 rc = SyHashInsert(&pTarget->hVar, pEntry->pKey, pEntry->nKeyLen, pEntry->pUserData);
469 return rc;
470}
471/*
472 * Leave the top-most active frame.
473 */
474static void VmLeaveFrame(jx9_vm *pVm)
475{
476 VmFrame *pFrame = pVm->pFrame;
477 if( pFrame ){
478 /* Unlink from the list of active VM frame */
479 pVm->pFrame = pFrame->pParent;
480 if( pFrame->pParent ){
481 VmSlot *aSlot;
482 sxu32 n;
483 /* Restore local variable to the free pool so that they can be reused again */
484 aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal);
485 for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){
486 /* Unset the local variable */
487 jx9VmUnsetMemObj(&(*pVm), aSlot[n].nIdx);
488 }
489 }
490 /* Release internal containers */
491 SyHashRelease(&pFrame->hVar);
492 SySetRelease(&pFrame->sArg);
493 SySetRelease(&pFrame->sLocal);
494 /* Release the whole structure */
495 SyMemBackendPoolFree(&pVm->sAllocator, pFrame);
496 }
497}
498/*
499 * Compare two functions signature and return the comparison result.
500 */
501static int VmOverloadCompare(SyString *pFirst, SyString *pSecond)
502{
503 const char *zSend = &pSecond->zString[pSecond->nByte];
504 const char *zFend = &pFirst->zString[pFirst->nByte];
505 const char *zSin = pSecond->zString;
506 const char *zFin = pFirst->zString;
507 const char *zPtr = zFin;
508 for(;;){
509 if( zFin >= zFend || zSin >= zSend ){
510 break;
511 }
512 if( zFin[0] != zSin[0] ){
513 /* mismatch */
514 break;
515 }
516 zFin++;
517 zSin++;
518 }
519 return (int)(zFin-zPtr);
520}
521/*
522 * Select the appropriate VM function for the current call context.
523 * This is the implementation of the powerful 'function overloading' feature
524 * introduced by the version 2 of the JX9 engine.
525 * Refer to the official documentation for more information.
526 */
527static jx9_vm_func * VmOverload(
528 jx9_vm *pVm, /* Target VM */
529 jx9_vm_func *pList, /* Linked list of candidates for overloading */
530 jx9_value *aArg, /* Array of passed arguments */
531 int nArg /* Total number of passed arguments */
532 )
533{
534 int iTarget, i, j, iCur, iMax;
535 jx9_vm_func *apSet[10]; /* Maximum number of candidates */
536 jx9_vm_func *pLink;
537 SyString sArgSig;
538 SyBlob sSig;
539
540 pLink = pList;
541 i = 0;
542 /* Put functions expecting the same number of passed arguments */
543 while( i < (int)SX_ARRAYSIZE(apSet) ){
544 if( pLink == 0 ){
545 break;
546 }
547 if( (int)SySetUsed(&pLink->aArgs) == nArg ){
548 /* Candidate for overloading */
549 apSet[i++] = pLink;
550 }
551 /* Point to the next entry */
552 pLink = pLink->pNextName;
553 }
554 if( i < 1 ){
555 /* No candidates, return the head of the list */
556 return pList;
557 }
558 if( nArg < 1 || i < 2 ){
559 /* Return the only candidate */
560 return apSet[0];
561 }
562 /* Calculate function signature */
563 SyBlobInit(&sSig, &pVm->sAllocator);
564 for( j = 0 ; j < nArg ; j++ ){
565 int c = 'n'; /* null */
566 if( aArg[j].iFlags & MEMOBJ_HASHMAP ){
567 /* Hashmap */
568 c = 'h';
569 }else if( aArg[j].iFlags & MEMOBJ_BOOL ){
570 /* bool */
571 c = 'b';
572 }else if( aArg[j].iFlags & MEMOBJ_INT ){
573 /* int */
574 c = 'i';
575 }else if( aArg[j].iFlags & MEMOBJ_STRING ){
576 /* String */
577 c = 's';
578 }else if( aArg[j].iFlags & MEMOBJ_REAL ){
579 /* Float */
580 c = 'f';
581 }
582 if( c > 0 ){
583 SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
584 }
585 }
586 SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig));
587 iTarget = 0;
588 iMax = -1;
589 /* Select the appropriate function */
590 for( j = 0 ; j < i ; j++ ){
591 /* Compare the two signatures */
592 iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature);
593 if( iCur > iMax ){
594 iMax = iCur;
595 iTarget = j;
596 }
597 }
598 SyBlobRelease(&sSig);
599 /* Appropriate function for the current call context */
600 return apSet[iTarget];
601}
602/*
603 * Dummy read-only buffer used for slot reservation.
604 */
605static const char zDummy[sizeof(jx9_value)] = { 0 }; /* Must be >= sizeof(jx9_value) */
606/*
607 * Reserve a constant memory object.
608 * Return a pointer to the raw jx9_value on success. NULL on failure.
609 */
610JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex)
611{
612 jx9_value *pObj;
613 sxi32 rc;
614 if( pIndex ){
615 /* Object index in the object table */
616 *pIndex = SySetUsed(&pVm->aLitObj);
617 }
618 /* Reserve a slot for the new object */
619 rc = SySetPut(&pVm->aLitObj, (const void *)zDummy);
620 if( rc != SXRET_OK ){
621 /* If the supplied memory subsystem is so sick that we are unable to allocate
622 * a tiny chunk of memory, there is no much we can do here.
623 */
624 return 0;
625 }
626 pObj = (jx9_value *)SySetPeek(&pVm->aLitObj);
627 return pObj;
628}
629/*
630 * Reserve a memory object.
631 * Return a pointer to the raw jx9_value on success. NULL on failure.
632 */
633static jx9_value * VmReserveMemObj(jx9_vm *pVm, sxu32 *pIndex)
634{
635 jx9_value *pObj;
636 sxi32 rc;
637 if( pIndex ){
638 /* Object index in the object table */
639 *pIndex = SySetUsed(&pVm->aMemObj);
640 }
641 /* Reserve a slot for the new object */
642 rc = SySetPut(&pVm->aMemObj, (const void *)zDummy);
643 if( rc != SXRET_OK ){
644 /* If the supplied memory subsystem is so sick that we are unable to allocate
645 * a tiny chunk of memory, there is no much we can do here.
646 */
647 return 0;
648 }
649 pObj = (jx9_value *)SySetPeek(&pVm->aMemObj);
650 return pObj;
651}
652/* Forward declaration */
653static sxi32 VmEvalChunk(jx9_vm *pVm, jx9_context *pCtx, SyString *pChunk, int iFlags, int bTrueReturn);
654/*
655 * Built-in functions that cannot be implemented directly as foreign functions.
656 */
657#define JX9_BUILTIN_LIB \
658 "function scandir(string $directory, int $sort_order = SCANDIR_SORT_ASCENDING)"\
659 "{"\
660 " if( func_num_args() < 1 ){ return FALSE; }"\
661 " $aDir = [];"\
662 " $pHandle = opendir($directory);"\
663 " if( $pHandle == FALSE ){ return FALSE; }"\
664 " while(FALSE !== ($pEntry = readdir($pHandle)) ){"\
665 " $aDir[] = $pEntry;"\
666 " }"\
667 " closedir($pHandle);"\
668 " if( $sort_order == SCANDIR_SORT_DESCENDING ){"\
669 " rsort($aDir);"\
670 " }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\
671 " sort($aDir);"\
672 " }"\
673 " return $aDir;"\
674 "}"\
675 "function glob(string $pattern, int $iFlags = 0){"\
676 "/* Open the target directory */"\
677 "$zDir = dirname($pattern);"\
678 "if(!is_string($zDir) ){ $zDir = './'; }"\
679 "$pHandle = opendir($zDir);"\
680 "if( $pHandle == FALSE ){"\
681 " /* IO error while opening the current directory, return FALSE */"\
682 " return FALSE;"\
683 "}"\
684 "$pattern = basename($pattern);"\
685 "$pArray = []; /* Empty array */"\
686 "/* Loop throw available entries */"\
687 "while( FALSE !== ($pEntry = readdir($pHandle)) ){"\
688 " /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\
689 " $rc = strglob($pattern, $pEntry);"\
690 " if( $rc ){"\
691 " if( is_dir($pEntry) ){"\
692 " if( $iFlags & GLOB_MARK ){"\
693 " /* Adds a slash to each directory returned */"\
694 " $pEntry .= DIRECTORY_SEPARATOR;"\
695 " }"\
696 " }else if( $iFlags & GLOB_ONLYDIR ){"\
697 " /* Not a directory, ignore */"\
698 " continue;"\
699 " }"\
700 " /* Add the entry */"\
701 " $pArray[] = $pEntry;"\
702 " }"\
703 " }"\
704 "/* Close the handle */"\
705 "closedir($pHandle);"\
706 "if( ($iFlags & GLOB_NOSORT) == 0 ){"\
707 " /* Sort the array */"\
708 " sort($pArray);"\
709 "}"\
710 "if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\
711 " /* Return the search pattern if no files matching were found */"\
712 " $pArray[] = $pattern;"\
713 "}"\
714 "/* Return the created array */"\
715 "return $pArray;"\
716 "}"\
717 "/* Creates a temporary file */"\
718 "function tmpfile(){"\
719 " /* Extract the temp directory */"\
720 " $zTempDir = sys_get_temp_dir();"\
721 " if( strlen($zTempDir) < 1 ){"\
722 " /* Use the current dir */"\
723 " $zTempDir = '.';"\
724 " }"\
725 " /* Create the file */"\
726 " $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'JX9'.rand_str(12), 'w+');"\
727 " return $pHandle;"\
728 "}"\
729 "/* Creates a temporary filename */"\
730 "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */, string $zPrefix = 'JX9')"\
731 "{"\
732 " return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\
733 "}"\
734 "function max(){"\
735 " $pArgs = func_get_args();"\
736 " if( sizeof($pArgs) < 1 ){"\
737 " return null;"\
738 " }"\
739 " if( sizeof($pArgs) < 2 ){"\
740 " $pArg = $pArgs[0];"\
741 " if( !is_array($pArg) ){"\
742 " return $pArg; "\
743 " }"\
744 " if( sizeof($pArg) < 1 ){"\
745 " return null;"\
746 " }"\
747 " $pArg = array_copy($pArgs[0]);"\
748 " reset($pArg);"\
749 " $max = current($pArg);"\
750 " while( FALSE !== ($val = next($pArg)) ){"\
751 " if( $val > $max ){"\
752 " $max = $val;"\
753 " }"\
754 " }"\
755 " return $max;"\
756 " }"\
757 " $max = $pArgs[0];"\
758 " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
759 " $val = $pArgs[$i];"\
760 "if( $val > $max ){"\
761 " $max = $val;"\
762 "}"\
763 " }"\
764 " return $max;"\
765 "}"\
766 "function min(){"\
767 " $pArgs = func_get_args();"\
768 " if( sizeof($pArgs) < 1 ){"\
769 " return null;"\
770 " }"\
771 " if( sizeof($pArgs) < 2 ){"\
772 " $pArg = $pArgs[0];"\
773 " if( !is_array($pArg) ){"\
774 " return $pArg; "\
775 " }"\
776 " if( sizeof($pArg) < 1 ){"\
777 " return null;"\
778 " }"\
779 " $pArg = array_copy($pArgs[0]);"\
780 " reset($pArg);"\
781 " $min = current($pArg);"\
782 " while( FALSE !== ($val = next($pArg)) ){"\
783 " if( $val < $min ){"\
784 " $min = $val;"\
785 " }"\
786 " }"\
787 " return $min;"\
788 " }"\
789 " $min = $pArgs[0];"\
790 " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
791 " $val = $pArgs[$i];"\
792 "if( $val < $min ){"\
793 " $min = $val;"\
794 " }"\
795 " }"\
796 " return $min;"\
797 "}"
798/*
799 * Initialize a freshly allocated JX9 Virtual Machine so that we can
800 * start compiling the target JX9 program.
801 */
802JX9_PRIVATE sxi32 jx9VmInit(
803 jx9_vm *pVm, /* Initialize this */
804 jx9 *pEngine /* Master engine */
805 )
806{
807 SyString sBuiltin;
808 jx9_value *pObj;
809 sxi32 rc;
810 /* Zero the structure */
811 SyZero(pVm, sizeof(jx9_vm));
812 /* Initialize VM fields */
813 pVm->pEngine = &(*pEngine);
814 SyMemBackendInitFromParent(&pVm->sAllocator, &pEngine->sAllocator);
815 /* Instructions containers */
816 SySetInit(&pVm->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
817 SySetAlloc(&pVm->aByteCode, 0xFF);
818 pVm->pByteContainer = &pVm->aByteCode;
819 /* Object containers */
820 SySetInit(&pVm->aMemObj, &pVm->sAllocator, sizeof(jx9_value));
821 SySetAlloc(&pVm->aMemObj, 0xFF);
822 /* Virtual machine internal containers */
823 SyBlobInit(&pVm->sConsumer, &pVm->sAllocator);
824 SyBlobInit(&pVm->sWorker, &pVm->sAllocator);
825 SyBlobInit(&pVm->sArgv, &pVm->sAllocator);
826 SySetInit(&pVm->aLitObj, &pVm->sAllocator, sizeof(jx9_value));
827 SySetAlloc(&pVm->aLitObj, 0xFF);
828 SyHashInit(&pVm->hHostFunction, &pVm->sAllocator, 0, 0);
829 SyHashInit(&pVm->hFunction, &pVm->sAllocator, 0, 0);
830 SyHashInit(&pVm->hConstant, &pVm->sAllocator, 0, 0);
831 SyHashInit(&pVm->hSuper, &pVm->sAllocator, 0, 0);
832 SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot));
833 /* Configuration containers */
834 SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString));
835 SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString));
836 SySetInit(&pVm->aIncluded, &pVm->sAllocator, sizeof(SyString));
837 SySetInit(&pVm->aIOstream, &pVm->sAllocator, sizeof(jx9_io_stream *));
838 /* Error callbacks containers */
839 jx9MemObjInit(&(*pVm), &pVm->sAssertCallback);
840 /* Set a default recursion limit */
841#if defined(__WINNT__) || defined(__UNIXES__)
842 pVm->nMaxDepth = 32;
843#else
844 pVm->nMaxDepth = 16;
845#endif
846 /* Default assertion flags */
847 pVm->iAssertFlags = JX9_ASSERT_WARNING; /* Issue a warning for each failed assertion */
848 /* PRNG context */
849 SyRandomnessInit(&pVm->sPrng, 0, 0);
850 /* Install the null constant */
851 pObj = jx9VmReserveConstObj(&(*pVm), 0);
852 if( pObj == 0 ){
853 rc = SXERR_MEM;
854 goto Err;
855 }
856 jx9MemObjInit(pVm, pObj);
857 /* Install the boolean TRUE constant */
858 pObj = jx9VmReserveConstObj(&(*pVm), 0);
859 if( pObj == 0 ){
860 rc = SXERR_MEM;
861 goto Err;
862 }
863 jx9MemObjInitFromBool(pVm, pObj, 1);
864 /* Install the boolean FALSE constant */
865 pObj = jx9VmReserveConstObj(&(*pVm), 0);
866 if( pObj == 0 ){
867 rc = SXERR_MEM;
868 goto Err;
869 }
870 jx9MemObjInitFromBool(pVm, pObj, 0);
871 /* Create the global frame */
872 rc = VmEnterFrame(&(*pVm), 0, 0);
873 if( rc != SXRET_OK ){
874 goto Err;
875 }
876 /* Initialize the code generator */
877 rc = jx9InitCodeGenerator(pVm, pEngine->xConf.xErr, pEngine->xConf.pErrData);
878 if( rc != SXRET_OK ){
879 goto Err;
880 }
881 /* VM correctly initialized, set the magic number */
882 pVm->nMagic = JX9_VM_INIT;
883 SyStringInitFromBuf(&sBuiltin,JX9_BUILTIN_LIB, sizeof(JX9_BUILTIN_LIB)-1);
884 /* Compile the built-in library */
885 VmEvalChunk(&(*pVm), 0, &sBuiltin, 0, FALSE);
886 /* Reset the code generator */
887 jx9ResetCodeGenerator(&(*pVm), pEngine->xConf.xErr, pEngine->xConf.pErrData);
888 return SXRET_OK;
889Err:
890 SyMemBackendRelease(&pVm->sAllocator);
891 return rc;
892}
893/*
894 * Default VM output consumer callback.That is, all VM output is redirected to this
895 * routine which store the output in an internal blob.
896 * The output can be extracted later after program execution [jx9_vm_exec()] via
897 * the [jx9_vm_config()] interface with a configuration verb set to
898 * jx9VM_CONFIG_EXTRACT_OUTPUT.
899 * Refer to the official docurmentation for additional information.
900 * Note that for performance reason it's preferable to install a VM output
901 * consumer callback via (jx9VM_CONFIG_OUTPUT) rather than waiting for the VM
902 * to finish executing and extracting the output.
903 */
904JX9_PRIVATE sxi32 jx9VmBlobConsumer(
905 const void *pOut, /* VM Generated output*/
906 unsigned int nLen, /* Generated output length */
907 void *pUserData /* User private data */
908 )
909{
910 sxi32 rc;
911 /* Store the output in an internal BLOB */
912 rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
913 return rc;
914}
915#define VM_STACK_GUARD 16
916/*
917 * Allocate a new operand stack so that we can start executing
918 * our compiled JX9 program.
919 * Return a pointer to the operand stack (array of jx9_values)
920 * on success. NULL (Fatal error) on failure.
921 */
922static jx9_value * VmNewOperandStack(
923 jx9_vm *pVm, /* Target VM */
924 sxu32 nInstr /* Total numer of generated bytecode instructions */
925 )
926{
927 jx9_value *pStack;
928 /* No instruction ever pushes more than a single element onto the
929 ** stack and the stack never grows on successive executions of the
930 ** same loop. So the total number of instructions is an upper bound
931 ** on the maximum stack depth required.
932 **
933 ** Allocation all the stack space we will ever need.
934 */
935 nInstr += VM_STACK_GUARD;
936 pStack = (jx9_value *)SyMemBackendAlloc(&pVm->sAllocator, nInstr * sizeof(jx9_value));
937 if( pStack == 0 ){
938 return 0;
939 }
940 /* Initialize the operand stack */
941 while( nInstr > 0 ){
942 jx9MemObjInit(&(*pVm), &pStack[nInstr - 1]);
943 --nInstr;
944 }
945 /* Ready for bytecode execution */
946 return pStack;
947}
948/* Forward declaration */
949static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm);
950/*
951 * Prepare the Virtual Machine for bytecode execution.
952 * This routine gets called by the JX9 engine after
953 * successful compilation of the target JX9 program.
954 */
955JX9_PRIVATE sxi32 jx9VmMakeReady(
956 jx9_vm *pVm /* Target VM */
957 )
958{
959 sxi32 rc;
960 if( pVm->nMagic != JX9_VM_INIT ){
961 /* Initialize your VM first */
962 return SXERR_CORRUPT;
963 }
964 /* Mark the VM ready for bytecode execution */
965 pVm->nMagic = JX9_VM_RUN;
966 /* Release the code generator now we have compiled our program */
967 jx9ResetCodeGenerator(pVm, 0, 0);
968 /* Emit the DONE instruction */
969 rc = jx9VmEmitInstr(&(*pVm), JX9_OP_DONE, 0, 0, 0, 0);
970 if( rc != SXRET_OK ){
971 return SXERR_MEM;
972 }
973 /* Script return value */
974 jx9MemObjInit(&(*pVm), &pVm->sExec); /* Assume a NULL return value */
975 /* Allocate a new operand stack */
976 pVm->aOps = VmNewOperandStack(&(*pVm), SySetUsed(pVm->pByteContainer));
977 if( pVm->aOps == 0 ){
978 return SXERR_MEM;
979 }
980 /* Set the default VM output consumer callback and it's
981 * private data. */
982 pVm->sVmConsumer.xConsumer = jx9VmBlobConsumer;
983 pVm->sVmConsumer.pUserData = &pVm->sConsumer;
984 /* Register special functions first [i.e: print, func_get_args(), die, etc.] */
985 rc = VmRegisterSpecialFunction(&(*pVm));
986 if( rc != SXRET_OK ){
987 /* Don't worry about freeing memory, everything will be released shortly */
988 return rc;
989 }
990 /* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */
991 rc = jx9HashmapLoadBuiltin(&(*pVm));
992 if( rc != SXRET_OK ){
993 /* Don't worry about freeing memory, everything will be released shortly */
994 return rc;
995 }
996 /* Register built-in constants [i.e: JX9_EOL, JX9_OS...] */
997 jx9RegisterBuiltInConstant(&(*pVm));
998 /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */
999 jx9RegisterBuiltInFunction(&(*pVm));
1000 /* VM is ready for bytecode execution */
1001 return SXRET_OK;
1002}
1003/*
1004 * Reset a Virtual Machine to it's initial state.
1005 */
1006JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm)
1007{
1008 if( pVm->nMagic != JX9_VM_RUN && pVm->nMagic != JX9_VM_EXEC ){
1009 return SXERR_CORRUPT;
1010 }
1011 /* TICKET 1433-003: As of this version, the VM is automatically reset */
1012 SyBlobReset(&pVm->sConsumer);
1013 jx9MemObjRelease(&pVm->sExec);
1014 /* Set the ready flag */
1015 pVm->nMagic = JX9_VM_RUN;
1016 return SXRET_OK;
1017}
1018/*
1019 * Release a Virtual Machine.
1020 * Every virtual machine must be destroyed in order to avoid memory leaks.
1021 */
1022JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm)
1023{
1024 /* Set the stale magic number */
1025 pVm->nMagic = JX9_VM_STALE;
1026 /* Release the private memory subsystem */
1027 SyMemBackendRelease(&pVm->sAllocator);
1028 return SXRET_OK;
1029}
1030/*
1031 * Initialize a foreign function call context.
1032 * The context in which a foreign function executes is stored in a jx9_context object.
1033 * A pointer to a jx9_context object is always first parameter to application-defined foreign
1034 * functions.
1035 * The application-defined foreign function implementation will pass this pointer through into
1036 * calls to dozens of interfaces, these includes jx9_result_int(), jx9_result_string(), jx9_result_value(),
1037 * jx9_context_new_scalar(), jx9_context_alloc_chunk(), jx9_context_output(), jx9_context_throw_error()
1038 * and many more. Refer to the C/C++ Interfaces documentation for additional information.
1039 */
1040static sxi32 VmInitCallContext(
1041 jx9_context *pOut, /* Call Context */
1042 jx9_vm *pVm, /* Target VM */
1043 jx9_user_func *pFunc, /* Foreign function to execute shortly */
1044 jx9_value *pRet, /* Store return value here*/
1045 sxi32 iFlags /* Control flags */
1046 )
1047{
1048 pOut->pFunc = pFunc;
1049 pOut->pVm = pVm;
1050 SySetInit(&pOut->sVar, &pVm->sAllocator, sizeof(jx9_value *));
1051 SySetInit(&pOut->sChunk, &pVm->sAllocator, sizeof(jx9_aux_data));
1052 /* Assume a null return value */
1053 MemObjSetType(pRet, MEMOBJ_NULL);
1054 pOut->pRet = pRet;
1055 pOut->iFlags = iFlags;
1056 return SXRET_OK;
1057}
1058/*
1059 * Release a foreign function call context and cleanup the mess
1060 * left behind.
1061 */
1062static void VmReleaseCallContext(jx9_context *pCtx)
1063{
1064 sxu32 n;
1065 if( SySetUsed(&pCtx->sVar) > 0 ){
1066 jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
1067 for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
1068 if( apObj[n] == 0 ){
1069 /* Already released */
1070 continue;
1071 }
1072 jx9MemObjRelease(apObj[n]);
1073 SyMemBackendPoolFree(&pCtx->pVm->sAllocator, apObj[n]);
1074 }
1075 SySetRelease(&pCtx->sVar);
1076 }
1077 if( SySetUsed(&pCtx->sChunk) > 0 ){
1078 jx9_aux_data *aAux;
1079 void *pChunk;
1080 /* Automatic release of dynamically allocated chunk
1081 * using [jx9_context_alloc_chunk()].
1082 */
1083 aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
1084 for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
1085 pChunk = aAux[n].pAuxData;
1086 /* Release the chunk */
1087 if( pChunk ){
1088 SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
1089 }
1090 }
1091 SySetRelease(&pCtx->sChunk);
1092 }
1093}
1094/*
1095 * Release a jx9_value allocated from the body of a foreign function.
1096 * Refer to [jx9_context_release_value()] for additional information.
1097 */
1098JX9_PRIVATE void jx9VmReleaseContextValue(
1099 jx9_context *pCtx, /* Call context */
1100 jx9_value *pValue /* Release this value */
1101 )
1102{
1103 if( pValue == 0 ){
1104 /* NULL value is a harmless operation */
1105 return;
1106 }
1107 if( SySetUsed(&pCtx->sVar) > 0 ){
1108 jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
1109 sxu32 n;
1110 for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
1111 if( apObj[n] == pValue ){
1112 jx9MemObjRelease(pValue);
1113 SyMemBackendPoolFree(&pCtx->pVm->sAllocator, pValue);
1114 /* Mark as released */
1115 apObj[n] = 0;
1116 break;
1117 }
1118 }
1119 }
1120}
1121/*
1122 * Pop and release as many memory object from the operand stack.
1123 */
1124static void VmPopOperand(
1125 jx9_value **ppTos, /* Operand stack */
1126 sxi32 nPop /* Total number of memory objects to pop */
1127 )
1128{
1129 jx9_value *pTos = *ppTos;
1130 while( nPop > 0 ){
1131 jx9MemObjRelease(pTos);
1132 pTos--;
1133 nPop--;
1134 }
1135 /* Top of the stack */
1136 *ppTos = pTos;
1137}
1138/*
1139 * Reserve a memory object.
1140 * Return a pointer to the raw jx9_value on success. NULL on failure.
1141 */
1142JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIdx)
1143{
1144 jx9_value *pObj = 0;
1145 VmSlot *pSlot;
1146 sxu32 nIdx;
1147 /* Check for a free slot */
1148 nIdx = SXU32_HIGH; /* cc warning */
1149 pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj);
1150 if( pSlot ){
1151 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx);
1152 nIdx = pSlot->nIdx;
1153 }
1154 if( pObj == 0 ){
1155 /* Reserve a new memory object */
1156 pObj = VmReserveMemObj(&(*pVm), &nIdx);
1157 if( pObj == 0 ){
1158 return 0;
1159 }
1160 }
1161 /* Set a null default value */
1162 jx9MemObjInit(&(*pVm), pObj);
1163 if( pIdx ){
1164 *pIdx = nIdx;
1165 }
1166 pObj->nIdx = nIdx;
1167 return pObj;
1168}
1169/*
1170 * Extract a variable value from the top active VM frame.
1171 * Return a pointer to the variable value on success.
1172 * NULL otherwise (non-existent variable/Out-of-memory, ...).
1173 */
1174static jx9_value * VmExtractMemObj(
1175 jx9_vm *pVm, /* Target VM */
1176 const SyString *pName, /* Variable name */
1177 int bDup, /* True to duplicate variable name */
1178 int bCreate /* True to create the variable if non-existent */
1179 )
1180{
1181 int bNullify = FALSE;
1182 SyHashEntry *pEntry;
1183 VmFrame *pFrame;
1184 jx9_value *pObj;
1185 sxu32 nIdx;
1186 sxi32 rc;
1187 /* Point to the top active frame */
1188 pFrame = pVm->pFrame;
1189 /* Perform the lookup */
1190 if( pName == 0 || pName->nByte < 1 ){
1191 static const SyString sAnnon = { " " , sizeof(char) };
1192 pName = &sAnnon;
1193 /* Always nullify the object */
1194 bNullify = TRUE;
1195 bDup = FALSE;
1196 }
1197 /* Check the superglobals table first */
1198 pEntry = SyHashGet(&pVm->hSuper, (const void *)pName->zString, pName->nByte);
1199 if( pEntry == 0 ){
1200 /* Query the top active frame */
1201 pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
1202 if( pEntry == 0 ){
1203 char *zName = (char *)pName->zString;
1204 VmSlot sLocal;
1205 if( !bCreate ){
1206 /* Do not create the variable, return NULL */
1207 return 0;
1208 }
1209 /* No such variable, automatically create a new one and install
1210 * it in the current frame.
1211 */
1212 pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
1213 if( pObj == 0 ){
1214 return 0;
1215 }
1216 if( bDup ){
1217 /* Duplicate name */
1218 zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
1219 if( zName == 0 ){
1220 return 0;
1221 }
1222 }
1223 /* Link to the top active VM frame */
1224 rc = SyHashInsert(&pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx));
1225 if( rc != SXRET_OK ){
1226 /* Return the slot to the free pool */
1227 sLocal.nIdx = nIdx;
1228 sLocal.pUserData = 0;
1229 SySetPut(&pVm->aFreeObj, (const void *)&sLocal);
1230 return 0;
1231 }
1232 if( pFrame->pParent != 0 ){
1233 /* Local variable */
1234 sLocal.nIdx = nIdx;
1235 SySetPut(&pFrame->sLocal, (const void *)&sLocal);
1236 }
1237 }else{
1238 /* Extract variable contents */
1239 nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
1240 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
1241 if( bNullify && pObj ){
1242 jx9MemObjRelease(pObj);
1243 }
1244 }
1245 }else{
1246 /* Superglobal */
1247 nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
1248 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
1249 }
1250 return pObj;
1251}
1252/*
1253 * Extract a superglobal variable such as $_GET, $_POST, $_HEADERS, ....
1254 * Return a pointer to the variable value on success.NULL otherwise.
1255 */
1256static jx9_value * VmExtractSuper(
1257 jx9_vm *pVm, /* Target VM */
1258 const char *zName, /* Superglobal name: NOT NULL TERMINATED */
1259 sxu32 nByte /* zName length */
1260 )
1261{
1262 SyHashEntry *pEntry;
1263 jx9_value *pValue;
1264 sxu32 nIdx;
1265 /* Query the superglobal table */
1266 pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
1267 if( pEntry == 0 ){
1268 /* No such entry */
1269 return 0;
1270 }
1271 /* Extract the superglobal index in the global object pool */
1272 nIdx = SX_PTR_TO_INT(pEntry->pUserData);
1273 /* Extract the variable value */
1274 pValue = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
1275 return pValue;
1276}
1277/*
1278 * Perform a raw hashmap insertion.
1279 * Refer to the [jx9VmConfigure()] implementation for additional information.
1280 */
1281static sxi32 VmHashmapInsert(
1282 jx9_hashmap *pMap, /* Target hashmap */
1283 const char *zKey, /* Entry key */
1284 int nKeylen, /* zKey length*/
1285 const char *zData, /* Entry data */
1286 int nLen /* zData length */
1287 )
1288{
1289 jx9_value sKey,sValue;
1290 jx9_value *pKey;
1291 sxi32 rc;
1292 pKey = 0;
1293 jx9MemObjInit(pMap->pVm, &sKey);
1294 jx9MemObjInitFromString(pMap->pVm, &sValue, 0);
1295 if( zKey ){
1296 if( nKeylen < 0 ){
1297 nKeylen = (int)SyStrlen(zKey);
1298 }
1299 jx9MemObjStringAppend(&sKey, zKey, (sxu32)nKeylen);
1300 pKey = &sKey;
1301 }
1302 if( zData ){
1303 if( nLen < 0 ){
1304 /* Compute length automatically */
1305 nLen = (int)SyStrlen(zData);
1306 }
1307 jx9MemObjStringAppend(&sValue, zData, (sxu32)nLen);
1308 }
1309 /* Perform the insertion */
1310 rc = jx9HashmapInsert(&(*pMap),pKey,&sValue);
1311 jx9MemObjRelease(&sKey);
1312 jx9MemObjRelease(&sValue);
1313 return rc;
1314}
1315/* Forward declaration */
1316static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte);
1317/*
1318 * Configure a working virtual machine instance.
1319 *
1320 * This routine is used to configure a JX9 virtual machine obtained by a prior
1321 * successful call to one of the compile interface such as jx9_compile()
1322 * jx9_compile_v2() or jx9_compile_file().
1323 * The second argument to this function is an integer configuration option
1324 * that determines what property of the JX9 virtual machine is to be configured.
1325 * Subsequent arguments vary depending on the configuration option in the second
1326 * argument. There are many verbs but the most important are JX9_VM_CONFIG_OUTPUT,
1327 * JX9_VM_CONFIG_HTTP_REQUEST and JX9_VM_CONFIG_ARGV_ENTRY.
1328 * Refer to the official documentation for the list of allowed verbs.
1329 */
1330JX9_PRIVATE sxi32 jx9VmConfigure(
1331 jx9_vm *pVm, /* Target VM */
1332 sxi32 nOp, /* Configuration verb */
1333 va_list ap /* Subsequent option arguments */
1334 )
1335{
1336 sxi32 rc = SXRET_OK;
1337 switch(nOp){
1338 case JX9_VM_CONFIG_OUTPUT: {
1339 ProcConsumer xConsumer = va_arg(ap, ProcConsumer);
1340 void *pUserData = va_arg(ap, void *);
1341 /* VM output consumer callback */
1342#ifdef UNTRUST
1343 if( xConsumer == 0 ){
1344 rc = SXERR_CORRUPT;
1345 break;
1346 }
1347#endif
1348 /* Install the output consumer */
1349 pVm->sVmConsumer.xConsumer = xConsumer;
1350 pVm->sVmConsumer.pUserData = pUserData;
1351 break;
1352 }
1353 case JX9_VM_CONFIG_IMPORT_PATH: {
1354 /* Import path */
1355 const char *zPath;
1356 SyString sPath;
1357 zPath = va_arg(ap, const char *);
1358#if defined(UNTRUST)
1359 if( zPath == 0 ){
1360 rc = SXERR_EMPTY;
1361 break;
1362 }
1363#endif
1364 SyStringInitFromBuf(&sPath, zPath, SyStrlen(zPath));
1365 /* Remove trailing slashes and backslashes */
1366#ifdef __WINNT__
1367 SyStringTrimTrailingChar(&sPath, '\\');
1368#endif
1369 SyStringTrimTrailingChar(&sPath, '/');
1370 /* Remove leading and trailing white spaces */
1371 SyStringFullTrim(&sPath);
1372 if( sPath.nByte > 0 ){
1373 /* Store the path in the corresponding conatiner */
1374 rc = SySetPut(&pVm->aPaths, (const void *)&sPath);
1375 }
1376 break;
1377 }
1378 case JX9_VM_CONFIG_ERR_REPORT:
1379 /* Run-Time Error report */
1380 pVm->bErrReport = 1;
1381 break;
1382 case JX9_VM_CONFIG_RECURSION_DEPTH:{
1383 /* Recursion depth */
1384 int nDepth = va_arg(ap, int);
1385 if( nDepth > 2 && nDepth < 1024 ){
1386 pVm->nMaxDepth = nDepth;
1387 }
1388 break;
1389 }
1390 case JX9_VM_OUTPUT_LENGTH: {
1391 /* VM output length in bytes */
1392 sxu32 *pOut = va_arg(ap, sxu32 *);
1393#ifdef UNTRUST
1394 if( pOut == 0 ){
1395 rc = SXERR_CORRUPT;
1396 break;
1397 }
1398#endif
1399 *pOut = pVm->nOutputLen;
1400 break;
1401 }
1402 case JX9_VM_CONFIG_CREATE_VAR: {
1403 /* Create a new superglobal/global variable */
1404 const char *zName = va_arg(ap, const char *);
1405 jx9_value *pValue = va_arg(ap, jx9_value *);
1406 SyHashEntry *pEntry;
1407 jx9_value *pObj;
1408 sxu32 nByte;
1409 sxu32 nIdx;
1410#ifdef UNTRUST
1411 if( SX_EMPTY_STR(zName) || pValue == 0 ){
1412 rc = SXERR_CORRUPT;
1413 break;
1414 }
1415#endif
1416 nByte = SyStrlen(zName);
1417 /* Check if the superglobal is already installed */
1418 pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
1419 if( pEntry ){
1420 /* Variable already installed */
1421 nIdx = SX_PTR_TO_INT(pEntry->pUserData);
1422 /* Extract contents */
1423 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
1424 if( pObj ){
1425 /* Overwrite old contents */
1426 jx9MemObjStore(pValue, pObj);
1427 }
1428 }else{
1429 /* Install a new variable */
1430 pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
1431 if( pObj == 0 ){
1432 rc = SXERR_MEM;
1433 break;
1434 }
1435 /* Copy value */
1436 jx9MemObjStore(pValue, pObj);
1437 /* Install the superglobal */
1438 rc = SyHashInsert(&pVm->hSuper, (const void *)zName, nByte, SX_INT_TO_PTR(nIdx));
1439 }
1440 break;
1441 }
1442 case JX9_VM_CONFIG_SERVER_ATTR:
1443 case JX9_VM_CONFIG_ENV_ATTR: {
1444 const char *zKey = va_arg(ap, const char *);
1445 const char *zValue = va_arg(ap, const char *);
1446 int nLen = va_arg(ap, int);
1447 jx9_hashmap *pMap;
1448 jx9_value *pValue;
1449 if( nOp == JX9_VM_CONFIG_ENV_ATTR ){
1450 /* Extract the $_ENV superglobal */
1451 pValue = VmExtractSuper(&(*pVm), "_ENV", sizeof("_ENV")-1);
1452 }else{
1453 /* Extract the $_SERVER superglobal */
1454 pValue = VmExtractSuper(&(*pVm), "_SERVER", sizeof("_SERVER")-1);
1455 }
1456 if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
1457 /* No such entry */
1458 rc = SXERR_NOTFOUND;
1459 break;
1460 }
1461 /* Point to the hashmap */
1462 pMap = (jx9_hashmap *)pValue->x.pOther;
1463 /* Perform the insertion */
1464 rc = VmHashmapInsert(pMap, zKey, -1, zValue, nLen);
1465 break;
1466 }
1467 case JX9_VM_CONFIG_ARGV_ENTRY:{
1468 /* Script arguments */
1469 const char *zValue = va_arg(ap, const char *);
1470 jx9_hashmap *pMap;
1471 jx9_value *pValue;
1472 /* Extract the $argv array */
1473 pValue = VmExtractSuper(&(*pVm), "argv", sizeof("argv")-1);
1474 if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
1475 /* No such entry */
1476 rc = SXERR_NOTFOUND;
1477 break;
1478 }
1479 /* Point to the hashmap */
1480 pMap = (jx9_hashmap *)pValue->x.pOther;
1481 /* Perform the insertion */
1482 rc = VmHashmapInsert(pMap, 0, 0, zValue,-1);
1483 if( rc == SXRET_OK && zValue && zValue[0] != 0 ){
1484 if( pMap->nEntry > 1 ){
1485 /* Append space separator first */
1486 SyBlobAppend(&pVm->sArgv, (const void *)" ", sizeof(char));
1487 }
1488 SyBlobAppend(&pVm->sArgv, (const void *)zValue,SyStrlen(zValue));
1489 }
1490 break;
1491 }
1492 case JX9_VM_CONFIG_EXEC_VALUE: {
1493 /* Script return value */
1494 jx9_value **ppValue = va_arg(ap, jx9_value **);
1495#ifdef UNTRUST
1496 if( ppValue == 0 ){
1497 rc = SXERR_CORRUPT;
1498 break;
1499 }
1500#endif
1501 *ppValue = &pVm->sExec;
1502 break;
1503 }
1504 case JX9_VM_CONFIG_IO_STREAM: {
1505 /* Register an IO stream device */
1506 const jx9_io_stream *pStream = va_arg(ap, const jx9_io_stream *);
1507 /* Make sure we are dealing with a valid IO stream */
1508 if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 ||
1509 pStream->xOpen == 0 || pStream->xRead == 0 ){
1510 /* Invalid stream */
1511 rc = SXERR_INVALID;
1512 break;
1513 }
1514 if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName, "file", sizeof("file")-1) == 0 ){
1515 /* Make the 'file://' stream the defaut stream device */
1516 pVm->pDefStream = pStream;
1517 }
1518 /* Insert in the appropriate container */
1519 rc = SySetPut(&pVm->aIOstream, (const void *)&pStream);
1520 break;
1521 }
1522 case JX9_VM_CONFIG_EXTRACT_OUTPUT: {
1523 /* Point to the VM internal output consumer buffer */
1524 const void **ppOut = va_arg(ap, const void **);
1525 unsigned int *pLen = va_arg(ap, unsigned int *);
1526#ifdef UNTRUST
1527 if( ppOut == 0 || pLen == 0 ){
1528 rc = SXERR_CORRUPT;
1529 break;
1530 }
1531#endif
1532 *ppOut = SyBlobData(&pVm->sConsumer);
1533 *pLen = SyBlobLength(&pVm->sConsumer);
1534 break;
1535 }
1536 case JX9_VM_CONFIG_HTTP_REQUEST:{
1537 /* Raw HTTP request*/
1538 const char *zRequest = va_arg(ap, const char *);
1539 int nByte = va_arg(ap, int);
1540 if( SX_EMPTY_STR(zRequest) ){
1541 rc = SXERR_EMPTY;
1542 break;
1543 }
1544 if( nByte < 0 ){
1545 /* Compute length automatically */
1546 nByte = (int)SyStrlen(zRequest);
1547 }
1548 /* Process the request */
1549 rc = VmHttpProcessRequest(&(*pVm), zRequest, nByte);
1550 break;
1551 }
1552 default:
1553 /* Unknown configuration option */
1554 rc = SXERR_UNKNOWN;
1555 break;
1556 }
1557 return rc;
1558}
1559/* Forward declaration */
1560static const char * VmInstrToString(sxi32 nOp);
1561/*
1562 * This routine is used to dump JX9 bytecode instructions to a human readable
1563 * format.
1564 * The dump is redirected to the given consumer callback which is responsible
1565 * of consuming the generated dump perhaps redirecting it to its standard output
1566 * (STDOUT).
1567 */
1568static sxi32 VmByteCodeDump(
1569 SySet *pByteCode, /* Bytecode container */
1570 ProcConsumer xConsumer, /* Dump consumer callback */
1571 void *pUserData /* Last argument to xConsumer() */
1572 )
1573{
1574 static const char zDump[] = {
1575 "====================================================\n"
1576 "JX9 VM Dump Copyright (C) 2012-2013 Symisc Systems\n"
1577 " http://jx9.symisc.net/\n"
1578 "====================================================\n"
1579 };
1580 VmInstr *pInstr, *pEnd;
1581 sxi32 rc = SXRET_OK;
1582 sxu32 n;
1583 /* Point to the JX9 instructions */
1584 pInstr = (VmInstr *)SySetBasePtr(pByteCode);
1585 pEnd = &pInstr[SySetUsed(pByteCode)];
1586 n = 0;
1587 xConsumer((const void *)zDump, sizeof(zDump)-1, pUserData);
1588 /* Dump instructions */
1589 for(;;){
1590 if( pInstr >= pEnd ){
1591 /* No more instructions */
1592 break;
1593 }
1594 /* Format and call the consumer callback */
1595 rc = SyProcFormat(xConsumer, pUserData, "%s %8d %8u %#8x [%u]\n",
1596 VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2,
1597 SX_PTR_TO_INT(pInstr->p3), n);
1598 if( rc != SXRET_OK ){
1599 /* Consumer routine request an operation abort */
1600 return rc;
1601 }
1602 ++n;
1603 pInstr++; /* Next instruction in the stream */
1604 }
1605 return rc;
1606}
1607/*
1608 * Consume a generated run-time error message by invoking the VM output
1609 * consumer callback.
1610 */
1611static sxi32 VmCallErrorHandler(jx9_vm *pVm, SyBlob *pMsg)
1612{
1613 jx9_output_consumer *pCons = &pVm->sVmConsumer;
1614 sxi32 rc = SXRET_OK;
1615 /* Append a new line */
1616#ifdef __WINNT__
1617 SyBlobAppend(pMsg, "\r\n", sizeof("\r\n")-1);
1618#else
1619 SyBlobAppend(pMsg, "\n", sizeof(char));
1620#endif
1621 /* Invoke the output consumer callback */
1622 rc = pCons->xConsumer(SyBlobData(pMsg), SyBlobLength(pMsg), pCons->pUserData);
1623 /* Increment output length */
1624 pVm->nOutputLen += SyBlobLength(pMsg);
1625
1626 return rc;
1627}
1628/*
1629 * Throw a run-time error and invoke the supplied VM output consumer callback.
1630 * Refer to the implementation of [jx9_context_throw_error()] for additional
1631 * information.
1632 */
1633JX9_PRIVATE sxi32 jx9VmThrowError(
1634 jx9_vm *pVm, /* Target VM */
1635 SyString *pFuncName, /* Function name. NULL otherwise */
1636 sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice]*/
1637 const char *zMessage /* Null terminated error message */
1638 )
1639{
1640 SyBlob *pWorker = &pVm->sWorker;
1641 SyString *pFile;
1642 char *zErr;
1643 sxi32 rc;
1644 if( !pVm->bErrReport ){
1645 /* Don't bother reporting errors */
1646 return SXRET_OK;
1647 }
1648 /* Reset the working buffer */
1649 SyBlobReset(pWorker);
1650 /* Peek the processed file if available */
1651 pFile = (SyString *)SySetPeek(&pVm->aFiles);
1652 if( pFile ){
1653 /* Append file name */
1654 SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
1655 SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
1656 }
1657 zErr = "Error: ";
1658 switch(iErr){
1659 case JX9_CTX_WARNING: zErr = "Warning: "; break;
1660 case JX9_CTX_NOTICE: zErr = "Notice: "; break;
1661 default:
1662 iErr = JX9_CTX_ERR;
1663 break;
1664 }
1665 SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
1666 if( pFuncName ){
1667 /* Append function name first */
1668 SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
1669 SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
1670 }
1671 SyBlobAppend(pWorker, zMessage, SyStrlen(zMessage));
1672 /* Consume the error message */
1673 rc = VmCallErrorHandler(&(*pVm), pWorker);
1674 return rc;
1675}
1676/*
1677 * Format and throw a run-time error and invoke the supplied VM output consumer callback.
1678 * Refer to the implementation of [jx9_context_throw_error_format()] for additional
1679 * information.
1680 */
1681static sxi32 VmThrowErrorAp(
1682 jx9_vm *pVm, /* Target VM */
1683 SyString *pFuncName, /* Function name. NULL otherwise */
1684 sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice] */
1685 const char *zFormat, /* Format message */
1686 va_list ap /* Variable list of arguments */
1687 )
1688{
1689 SyBlob *pWorker = &pVm->sWorker;
1690 SyString *pFile;
1691 char *zErr;
1692 sxi32 rc;
1693 if( !pVm->bErrReport ){
1694 /* Don't bother reporting errors */
1695 return SXRET_OK;
1696 }
1697 /* Reset the working buffer */
1698 SyBlobReset(pWorker);
1699 /* Peek the processed file if available */
1700 pFile = (SyString *)SySetPeek(&pVm->aFiles);
1701 if( pFile ){
1702 /* Append file name */
1703 SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
1704 SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
1705 }
1706 zErr = "Error: ";
1707 switch(iErr){
1708 case JX9_CTX_WARNING: zErr = "Warning: "; break;
1709 case JX9_CTX_NOTICE: zErr = "Notice: "; break;
1710 default:
1711 iErr = JX9_CTX_ERR;
1712 break;
1713 }
1714 SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
1715 if( pFuncName ){
1716 /* Append function name first */
1717 SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
1718 SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
1719 }
1720 SyBlobFormatAp(pWorker, zFormat, ap);
1721 /* Consume the error message */
1722 rc = VmCallErrorHandler(&(*pVm), pWorker);
1723 return rc;
1724}
1725/*
1726 * Format and throw a run-time error and invoke the supplied VM output consumer callback.
1727 * Refer to the implementation of [jx9_context_throw_error_format()] for additional
1728 * information.
1729 * ------------------------------------
1730 * Simple boring wrapper function.
1731 * ------------------------------------
1732 */
1733static sxi32 VmErrorFormat(jx9_vm *pVm, sxi32 iErr, const char *zFormat, ...)
1734{
1735 va_list ap;
1736 sxi32 rc;
1737 va_start(ap, zFormat);
1738 rc = VmThrowErrorAp(&(*pVm), 0, iErr, zFormat, ap);
1739 va_end(ap);
1740 return rc;
1741}
1742/*
1743 * Format and throw a run-time error and invoke the supplied VM output consumer callback.
1744 * Refer to the implementation of [jx9_context_throw_error_format()] for additional
1745 * information.
1746 * ------------------------------------
1747 * Simple boring wrapper function.
1748 * ------------------------------------
1749 */
1750JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap)
1751{
1752 sxi32 rc;
1753 rc = VmThrowErrorAp(&(*pVm), &(*pFuncName), iErr, zFormat, ap);
1754 return rc;
1755}
1756/* Forward declaration */
1757static sxi32 VmLocalExec(jx9_vm *pVm,SySet *pByteCode,jx9_value *pResult);
1758/*
1759 * Execute as much of a JX9 bytecode program as we can then return.
1760 *
1761 * [jx9VmMakeReady()] must be called before this routine in order to
1762 * close the program with a final OP_DONE and to set up the default
1763 * consumer routines and other stuff. Refer to the implementation
1764 * of [jx9VmMakeReady()] for additional information.
1765 * If the installed VM output consumer callback ever returns JX9_ABORT
1766 * then the program execution is halted.
1767 * After this routine has finished, [jx9VmRelease()] or [jx9VmReset()]
1768 * should be used respectively to clean up the mess that was left behind
1769 * or to reset the VM to it's initial state.
1770 */
1771static sxi32 VmByteCodeExec(
1772 jx9_vm *pVm, /* Target VM */
1773 VmInstr *aInstr, /* JX9 bytecode program */
1774 jx9_value *pStack, /* Operand stack */
1775 int nTos, /* Top entry in the operand stack (usually -1) */
1776 jx9_value *pResult /* Store program return value here. NULL otherwise */
1777 )
1778{
1779 VmInstr *pInstr;
1780 jx9_value *pTos;
1781 SySet aArg;
1782 sxi32 pc;
1783 sxi32 rc;
1784 /* Argument container */
1785 SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
1786 if( nTos < 0 ){
1787 pTos = &pStack[-1];
1788 }else{
1789 pTos = &pStack[nTos];
1790 }
1791 pc = 0;
1792 /* Execute as much as we can */
1793 for(;;){
1794 /* Fetch the instruction to execute */
1795 pInstr = &aInstr[pc];
1796 rc = SXRET_OK;
1797/*
1798 * What follows here is a massive switch statement where each case implements a
1799 * separate instruction in the virtual machine. If we follow the usual
1800 * indentation convention each case should be indented by 6 spaces. But
1801 * that is a lot of wasted space on the left margin. So the code within
1802 * the switch statement will break with convention and be flush-left.
1803 */
1804 switch(pInstr->iOp){
1805/*
1806 * DONE: P1 * *
1807 *
1808 * Program execution completed: Clean up the mess left behind
1809 * and return immediately.
1810 */
1811case JX9_OP_DONE:
1812 if( pInstr->iP1 ){
1813#ifdef UNTRUST
1814 if( pTos < pStack ){
1815 goto Abort;
1816 }
1817#endif
1818 if( pResult ){
1819 /* Execution result */
1820 jx9MemObjStore(pTos, pResult);
1821 }
1822 VmPopOperand(&pTos, 1);
1823 }
1824 goto Done;
1825/*
1826 * HALT: P1 * *
1827 *
1828 * Program execution aborted: Clean up the mess left behind
1829 * and abort immediately.
1830 */
1831case JX9_OP_HALT:
1832 if( pInstr->iP1 ){
1833#ifdef UNTRUST
1834 if( pTos < pStack ){
1835 goto Abort;
1836 }
1837#endif
1838 if( pTos->iFlags & MEMOBJ_STRING ){
1839 if( SyBlobLength(&pTos->sBlob) > 0 ){
1840 /* Output the exit message */
1841 pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob),
1842 pVm->sVmConsumer.pUserData);
1843 /* Increment output length */
1844 pVm->nOutputLen += SyBlobLength(&pTos->sBlob);
1845 }
1846 }else if(pTos->iFlags & MEMOBJ_INT ){
1847 /* Record exit status */
1848 pVm->iExitStatus = (sxi32)pTos->x.iVal;
1849 }
1850 VmPopOperand(&pTos, 1);
1851 }
1852 goto Abort;
1853/*
1854 * JMP: * P2 *
1855 *
1856 * Unconditional jump: The next instruction executed will be
1857 * the one at index P2 from the beginning of the program.
1858 */
1859case JX9_OP_JMP:
1860 pc = pInstr->iP2 - 1;
1861 break;
1862/*
1863 * JZ: P1 P2 *
1864 *
1865 * Take the jump if the top value is zero (FALSE jump).Pop the top most
1866 * entry in the stack if P1 is zero.
1867 */
1868case JX9_OP_JZ:
1869#ifdef UNTRUST
1870 if( pTos < pStack ){
1871 goto Abort;
1872 }
1873#endif
1874 /* Get a boolean value */
1875 if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
1876 jx9MemObjToBool(pTos);
1877 }
1878 if( !pTos->x.iVal ){
1879 /* Take the jump */
1880 pc = pInstr->iP2 - 1;
1881 }
1882 if( !pInstr->iP1 ){
1883 VmPopOperand(&pTos, 1);
1884 }
1885 break;
1886/*
1887 * JNZ: P1 P2 *
1888 *
1889 * Take the jump if the top value is not zero (TRUE jump).Pop the top most
1890 * entry in the stack if P1 is zero.
1891 */
1892case JX9_OP_JNZ:
1893#ifdef UNTRUST
1894 if( pTos < pStack ){
1895 goto Abort;
1896 }
1897#endif
1898 /* Get a boolean value */
1899 if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
1900 jx9MemObjToBool(pTos);
1901 }
1902 if( pTos->x.iVal ){
1903 /* Take the jump */
1904 pc = pInstr->iP2 - 1;
1905 }
1906 if( !pInstr->iP1 ){
1907 VmPopOperand(&pTos, 1);
1908 }
1909 break;
1910/*
1911 * NOOP: * * *
1912 *
1913 * Do nothing. This instruction is often useful as a jump
1914 * destination.
1915 */
1916case JX9_OP_NOOP:
1917 break;
1918/*
1919 * POP: P1 * *
1920 *
1921 * Pop P1 elements from the operand stack.
1922 */
1923case JX9_OP_POP: {
1924 sxi32 n = pInstr->iP1;
1925 if( &pTos[-n+1] < pStack ){
1926 /* TICKET 1433-51 Stack underflow must be handled at run-time */
1927 n = (sxi32)(pTos - pStack);
1928 }
1929 VmPopOperand(&pTos, n);
1930 break;
1931 }
1932/*
1933 * CVT_INT: * * *
1934 *
1935 * Force the top of the stack to be an integer.
1936 */
1937case JX9_OP_CVT_INT:
1938#ifdef UNTRUST
1939 if( pTos < pStack ){
1940 goto Abort;
1941 }
1942#endif
1943 if((pTos->iFlags & MEMOBJ_INT) == 0 ){
1944 jx9MemObjToInteger(pTos);
1945 }
1946 /* Invalidate any prior representation */
1947 MemObjSetType(pTos, MEMOBJ_INT);
1948 break;
1949/*
1950 * CVT_REAL: * * *
1951 *
1952 * Force the top of the stack to be a real.
1953 */
1954case JX9_OP_CVT_REAL:
1955#ifdef UNTRUST
1956 if( pTos < pStack ){
1957 goto Abort;
1958 }
1959#endif
1960 if((pTos->iFlags & MEMOBJ_REAL) == 0 ){
1961 jx9MemObjToReal(pTos);
1962 }
1963 /* Invalidate any prior representation */
1964 MemObjSetType(pTos, MEMOBJ_REAL);
1965 break;
1966/*
1967 * CVT_STR: * * *
1968 *
1969 * Force the top of the stack to be a string.
1970 */
1971case JX9_OP_CVT_STR:
1972#ifdef UNTRUST
1973 if( pTos < pStack ){
1974 goto Abort;
1975 }
1976#endif
1977 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
1978 jx9MemObjToString(pTos);
1979 }
1980 break;
1981/*
1982 * CVT_BOOL: * * *
1983 *
1984 * Force the top of the stack to be a boolean.
1985 */
1986case JX9_OP_CVT_BOOL:
1987#ifdef UNTRUST
1988 if( pTos < pStack ){
1989 goto Abort;
1990 }
1991#endif
1992 if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
1993 jx9MemObjToBool(pTos);
1994 }
1995 break;
1996/*
1997 * CVT_NULL: * * *
1998 *
1999 * Nullify the top of the stack.
2000 */
2001case JX9_OP_CVT_NULL:
2002#ifdef UNTRUST
2003 if( pTos < pStack ){
2004 goto Abort;
2005 }
2006#endif
2007 jx9MemObjRelease(pTos);
2008 break;
2009/*
2010 * CVT_NUMC: * * *
2011 *
2012 * Force the top of the stack to be a numeric type (integer, real or both).
2013 */
2014case JX9_OP_CVT_NUMC:
2015#ifdef UNTRUST
2016 if( pTos < pStack ){
2017 goto Abort;
2018 }
2019#endif
2020 /* Force a numeric cast */
2021 jx9MemObjToNumeric(pTos);
2022 break;
2023/*
2024 * CVT_ARRAY: * * *
2025 *
2026 * Force the top of the stack to be a hashmap aka 'array'.
2027 */
2028case JX9_OP_CVT_ARRAY:
2029#ifdef UNTRUST
2030 if( pTos < pStack ){
2031 goto Abort;
2032 }
2033#endif
2034 /* Force a hashmap cast */
2035 rc = jx9MemObjToHashmap(pTos);
2036 if( rc != SXRET_OK ){
2037 /* Not so fatal, emit a simple warning */
2038 jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
2039 "JX9 engine is running out of memory while performing an array cast");
2040 }
2041 break;
2042/*
2043 * LOADC P1 P2 *
2044 *
2045 * Load a constant [i.e: JX9_EOL, JX9_OS, __TIME__, ...] indexed at P2 in the constant pool.
2046 * If P1 is set, then this constant is candidate for expansion via user installable callbacks.
2047 */
2048case JX9_OP_LOADC: {
2049 jx9_value *pObj;
2050 /* Reserve a room */
2051 pTos++;
2052 if( (pObj = (jx9_value *)SySetAt(&pVm->aLitObj, pInstr->iP2)) != 0 ){
2053 if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){
2054 SyHashEntry *pEntry;
2055 /* Candidate for expansion via user defined callbacks */
2056 pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
2057 if( pEntry ){
2058 jx9_constant *pCons = (jx9_constant *)pEntry->pUserData;
2059 /* Set a NULL default value */
2060 MemObjSetType(pTos, MEMOBJ_NULL);
2061 SyBlobReset(&pTos->sBlob);
2062 /* Invoke the callback and deal with the expanded value */
2063 pCons->xExpand(pTos, pCons->pUserData);
2064 /* Mark as constant */
2065 pTos->nIdx = SXU32_HIGH;
2066 break;
2067 }
2068 }
2069 jx9MemObjLoad(pObj, pTos);
2070 }else{
2071 /* Set a NULL value */
2072 MemObjSetType(pTos, MEMOBJ_NULL);
2073 }
2074 /* Mark as constant */
2075 pTos->nIdx = SXU32_HIGH;
2076 break;
2077 }
2078/*
2079 * LOAD: P1 * P3
2080 *
2081 * Load a variable where it's name is taken from the top of the stack or
2082 * from the P3 operand.
2083 * If P1 is set, then perform a lookup only.In other words do not create
2084 * the variable if non existent and push the NULL constant instead.
2085 */
2086case JX9_OP_LOAD:{
2087 jx9_value *pObj;
2088 SyString sName;
2089 if( pInstr->p3 == 0 ){
2090 /* Take the variable name from the top of the stack */
2091#ifdef UNTRUST
2092 if( pTos < pStack ){
2093 goto Abort;
2094 }
2095#endif
2096 /* Force a string cast */
2097 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
2098 jx9MemObjToString(pTos);
2099 }
2100 SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
2101 }else{
2102 SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
2103 /* Reserve a room for the target object */
2104 pTos++;
2105 }
2106 /* Extract the requested memory object */
2107 pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, pInstr->iP1 != 1);
2108 if( pObj == 0 ){
2109 if( pInstr->iP1 ){
2110 /* Variable not found, load NULL */
2111 if( !pInstr->p3 ){
2112 jx9MemObjRelease(pTos);
2113 }else{
2114 MemObjSetType(pTos, MEMOBJ_NULL);
2115 }
2116 pTos->nIdx = SXU32_HIGH; /* Mark as constant */
2117 break;
2118 }else{
2119 /* Fatal error */
2120 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
2121 goto Abort;
2122 }
2123 }
2124 /* Load variable contents */
2125 jx9MemObjLoad(pObj, pTos);
2126 pTos->nIdx = pObj->nIdx;
2127 break;
2128 }
2129/*
2130 * LOAD_MAP P1 * *
2131 *
2132 * Allocate a new empty hashmap (array in the JX9 jargon) and push it on the stack.
2133 * If the P1 operand is greater than zero then pop P1 elements from the
2134 * stack and insert them (key => value pair) in the new hashmap.
2135 */
2136case JX9_OP_LOAD_MAP: {
2137 jx9_hashmap *pMap;
2138 int is_json_object; /* TRUE if we are dealing with a JSON object */
2139 int iIncr = 1;
2140 /* Allocate a new hashmap instance */
2141 pMap = jx9NewHashmap(&(*pVm), 0, 0);
2142 if( pMap == 0 ){
2143 VmErrorFormat(&(*pVm), JX9_CTX_ERR,
2144 "Fatal, JX9 engine is running out of memory while loading JSON array/object at instruction #:%d", pc);
2145 goto Abort;
2146 }
2147 is_json_object = 0;
2148 if( pInstr->iP2 ){
2149 /* JSON object, record that */
2150 pMap->iFlags |= HASHMAP_JSON_OBJECT;
2151 is_json_object = 1;
2152 iIncr = 2;
2153 }
2154 if( pInstr->iP1 > 0 ){
2155 jx9_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */
2156 /* Perform the insertion */
2157 while( pEntry <= pTos ){
2158 /* Standard insertion */
2159 jx9HashmapInsert(pMap,
2160 is_json_object ? pEntry : 0 /* Automatic index assign */,
2161 is_json_object ? &pEntry[1] : pEntry
2162 );
2163 /* Next pair on the stack */
2164 pEntry += iIncr;
2165 }
2166 /* Pop P1 elements */
2167 VmPopOperand(&pTos, pInstr->iP1);
2168 }
2169 /* Push the hashmap */
2170 pTos++;
2171 pTos->x.pOther = pMap;
2172 MemObjSetType(pTos, MEMOBJ_HASHMAP);
2173 break;
2174 }
2175/*
2176 * LOAD_IDX: P1 P2 *
2177 *
2178 * Load a hasmap entry where it's index (either numeric or string) is taken
2179 * from the stack.
2180 * If the index does not refer to a valid element, then push the NULL constant
2181 * instead.
2182 */
2183case JX9_OP_LOAD_IDX: {
2184 jx9_hashmap_node *pNode = 0; /* cc warning */
2185 jx9_hashmap *pMap = 0;
2186 jx9_value *pIdx;
2187 pIdx = 0;
2188 if( pInstr->iP1 == 0 ){
2189 if( !pInstr->iP2){
2190 /* No available index, load NULL */
2191 if( pTos >= pStack ){
2192 jx9MemObjRelease(pTos);
2193 }else{
2194 /* TICKET 1433-020: Empty stack */
2195 pTos++;
2196 MemObjSetType(pTos, MEMOBJ_NULL);
2197 pTos->nIdx = SXU32_HIGH;
2198 }
2199 /* Emit a notice */
2200 jx9VmThrowError(&(*pVm), 0, JX9_CTX_NOTICE,
2201 "JSON Array/Object: Attempt to access an undefined member, JX9 is loading NULL");
2202 break;
2203 }
2204 }else{
2205 pIdx = pTos;
2206 pTos--;
2207 }
2208 if( pTos->iFlags & MEMOBJ_STRING ){
2209 /* String access */
2210 if( pIdx ){
2211 sxu32 nOfft;
2212 if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){
2213 /* Force an int cast */
2214 jx9MemObjToInteger(pIdx);
2215 }
2216 nOfft = (sxu32)pIdx->x.iVal;
2217 if( nOfft >= SyBlobLength(&pTos->sBlob) ){
2218 /* Invalid offset, load null */
2219 jx9MemObjRelease(pTos);
2220 }else{
2221 const char *zData = (const char *)SyBlobData(&pTos->sBlob);
2222 int c = zData[nOfft];
2223 jx9MemObjRelease(pTos);
2224 MemObjSetType(pTos, MEMOBJ_STRING);
2225 SyBlobAppend(&pTos->sBlob, (const void *)&c, sizeof(char));
2226 }
2227 }else{
2228 /* No available index, load NULL */
2229 MemObjSetType(pTos, MEMOBJ_NULL);
2230 }
2231 break;
2232 }
2233 if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){
2234 if( pTos->nIdx != SXU32_HIGH ){
2235 jx9_value *pObj;
2236 if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
2237 jx9MemObjToHashmap(pObj);
2238 jx9MemObjLoad(pObj, pTos);
2239 }
2240 }
2241 }
2242 rc = SXERR_NOTFOUND; /* Assume the index is invalid */
2243 if( pTos->iFlags & MEMOBJ_HASHMAP ){
2244 /* Point to the hashmap */
2245 pMap = (jx9_hashmap *)pTos->x.pOther;
2246 if( pIdx ){
2247 /* Load the desired entry */
2248 rc = jx9HashmapLookup(pMap, pIdx, &pNode);
2249 }
2250 if( rc != SXRET_OK && pInstr->iP2 ){
2251 /* Create a new empty entry */
2252 rc = jx9HashmapInsert(pMap, pIdx, 0);
2253 if( rc == SXRET_OK ){
2254 /* Point to the last inserted entry */
2255 pNode = pMap->pLast;
2256 }
2257 }
2258 }
2259 if( pIdx ){
2260 jx9MemObjRelease(pIdx);
2261 }
2262 if( rc == SXRET_OK ){
2263 /* Load entry contents */
2264 if( pMap->iRef < 2 ){
2265 /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
2266 * of the entry value, rather than pointing to it.
2267 */
2268 pTos->nIdx = SXU32_HIGH;
2269 jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
2270 }else{
2271 pTos->nIdx = pNode->nValIdx;
2272 jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
2273 jx9HashmapUnref(pMap);
2274 }
2275 }else{
2276 /* No such entry, load NULL */
2277 jx9MemObjRelease(pTos);
2278 pTos->nIdx = SXU32_HIGH;
2279 }
2280 break;
2281 }
2282/*
2283 * STORE * P2 P3
2284 *
2285 * Perform a store (Assignment) operation.
2286 */
2287case JX9_OP_STORE: {
2288 jx9_value *pObj;
2289 SyString sName;
2290#ifdef UNTRUST
2291 if( pTos < pStack ){
2292 goto Abort;
2293 }
2294#endif
2295 if( pInstr->iP2 ){
2296 sxu32 nIdx;
2297 /* Member store operation */
2298 nIdx = pTos->nIdx;
2299 VmPopOperand(&pTos, 1);
2300 if( nIdx == SXU32_HIGH ){
2301 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
2302 "Cannot perform assignment on a constant object attribute, JX9 is loading NULL");
2303 pTos->nIdx = SXU32_HIGH;
2304 }else{
2305 /* Point to the desired memory object */
2306 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
2307 if( pObj ){
2308 /* Perform the store operation */
2309 jx9MemObjStore(pTos, pObj);
2310 }
2311 }
2312 break;
2313 }else if( pInstr->p3 == 0 ){
2314 /* Take the variable name from the next on the stack */
2315 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
2316 /* Force a string cast */
2317 jx9MemObjToString(pTos);
2318 }
2319 SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
2320 pTos--;
2321#ifdef UNTRUST
2322 if( pTos < pStack ){
2323 goto Abort;
2324 }
2325#endif
2326 }else{
2327 SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
2328 }
2329 /* Extract the desired variable and if not available dynamically create it */
2330 pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, TRUE);
2331 if( pObj == 0 ){
2332 VmErrorFormat(&(*pVm), JX9_CTX_ERR,
2333 "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
2334 goto Abort;
2335 }
2336 if( !pInstr->p3 ){
2337 jx9MemObjRelease(&pTos[1]);
2338 }
2339 /* Perform the store operation */
2340 jx9MemObjStore(pTos, pObj);
2341 break;
2342 }
2343/*
2344 * STORE_IDX: P1 * P3
2345 *
2346 * Perfrom a store operation an a hashmap entry.
2347 */
2348case JX9_OP_STORE_IDX: {
2349 jx9_hashmap *pMap = 0; /* cc warning */
2350 jx9_value *pKey;
2351 sxu32 nIdx;
2352 if( pInstr->iP1 ){
2353 /* Key is next on stack */
2354 pKey = pTos;
2355 pTos--;
2356 }else{
2357 pKey = 0;
2358 }
2359 nIdx = pTos->nIdx;
2360 if( pTos->iFlags & MEMOBJ_HASHMAP ){
2361 /* Hashmap already loaded */
2362 pMap = (jx9_hashmap *)pTos->x.pOther;
2363 if( pMap->iRef < 2 ){
2364 /* TICKET 1433-48: Prevent garbage collection */
2365 pMap->iRef = 2;
2366 }
2367 }else{
2368 jx9_value *pObj;
2369 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
2370 if( pObj == 0 ){
2371 if( pKey ){
2372 jx9MemObjRelease(pKey);
2373 }
2374 VmPopOperand(&pTos, 1);
2375 break;
2376 }
2377 /* Phase#1: Load the array */
2378 if( (pObj->iFlags & MEMOBJ_STRING) ){
2379 VmPopOperand(&pTos, 1);
2380 if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){
2381 /* Force a string cast */
2382 jx9MemObjToString(pTos);
2383 }
2384 if( pKey == 0 ){
2385 /* Append string */
2386 if( SyBlobLength(&pTos->sBlob) > 0 ){
2387 SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
2388 }
2389 }else{
2390 sxu32 nOfft;
2391 if((pKey->iFlags & MEMOBJ_INT)){
2392 /* Force an int cast */
2393 jx9MemObjToInteger(pKey);
2394 }
2395 nOfft = (sxu32)pKey->x.iVal;
2396 if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){
2397 const char *zBlob = (const char *)SyBlobData(&pTos->sBlob);
2398 char *zData = (char *)SyBlobData(&pObj->sBlob);
2399 zData[nOfft] = zBlob[0];
2400 }else{
2401 if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){
2402 /* Perform an append operation */
2403 SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), sizeof(char));
2404 }
2405 }
2406 }
2407 if( pKey ){
2408 jx9MemObjRelease(pKey);
2409 }
2410 break;
2411 }else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
2412 /* Force a hashmap cast */
2413 rc = jx9MemObjToHashmap(pObj);
2414 if( rc != SXRET_OK ){
2415 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while creating a new array");
2416 goto Abort;
2417 }
2418 }
2419 pMap = (jx9_hashmap *)pObj->x.pOther;
2420 }
2421 VmPopOperand(&pTos, 1);
2422 /* Phase#2: Perform the insertion */
2423 jx9HashmapInsert(pMap, pKey, pTos);
2424 if( pKey ){
2425 jx9MemObjRelease(pKey);
2426 }
2427 break;
2428 }
2429/*
2430 * INCR: P1 * *
2431 *
2432 * Force a numeric cast and increment the top of the stack by 1.
2433 * If the P1 operand is set then perform a duplication of the top of
2434 * the stack and increment after that.
2435 */
2436case JX9_OP_INCR:
2437#ifdef UNTRUST
2438 if( pTos < pStack ){
2439 goto Abort;
2440 }
2441#endif
2442 if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){
2443 if( pTos->nIdx != SXU32_HIGH ){
2444 jx9_value *pObj;
2445 if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
2446 /* Force a numeric cast */
2447 jx9MemObjToNumeric(pObj);
2448 if( pObj->iFlags & MEMOBJ_REAL ){
2449 pObj->x.rVal++;
2450 /* Try to get an integer representation */
2451 jx9MemObjTryInteger(pTos);
2452 }else{
2453 pObj->x.iVal++;
2454 MemObjSetType(pTos, MEMOBJ_INT);
2455 }
2456 if( pInstr->iP1 ){
2457 /* Pre-icrement */
2458 jx9MemObjStore(pObj, pTos);
2459 }
2460 }
2461 }else{
2462 if( pInstr->iP1 ){
2463 /* Force a numeric cast */
2464 jx9MemObjToNumeric(pTos);
2465 /* Pre-increment */
2466 if( pTos->iFlags & MEMOBJ_REAL ){
2467 pTos->x.rVal++;
2468 /* Try to get an integer representation */
2469 jx9MemObjTryInteger(pTos);
2470 }else{
2471 pTos->x.iVal++;
2472 MemObjSetType(pTos, MEMOBJ_INT);
2473 }
2474 }
2475 }
2476 }
2477 break;
2478/*
2479 * DECR: P1 * *
2480 *
2481 * Force a numeric cast and decrement the top of the stack by 1.
2482 * If the P1 operand is set then perform a duplication of the top of the stack
2483 * and decrement after that.
2484 */
2485case JX9_OP_DECR:
2486#ifdef UNTRUST
2487 if( pTos < pStack ){
2488 goto Abort;
2489 }
2490#endif
2491 if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){
2492 /* Force a numeric cast */
2493 jx9MemObjToNumeric(pTos);
2494 if( pTos->nIdx != SXU32_HIGH ){
2495 jx9_value *pObj;
2496 if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
2497 /* Force a numeric cast */
2498 jx9MemObjToNumeric(pObj);
2499 if( pObj->iFlags & MEMOBJ_REAL ){
2500 pObj->x.rVal--;
2501 /* Try to get an integer representation */
2502 jx9MemObjTryInteger(pTos);
2503 }else{
2504 pObj->x.iVal--;
2505 MemObjSetType(pTos, MEMOBJ_INT);
2506 }
2507 if( pInstr->iP1 ){
2508 /* Pre-icrement */
2509 jx9MemObjStore(pObj, pTos);
2510 }
2511 }
2512 }else{
2513 if( pInstr->iP1 ){
2514 /* Pre-increment */
2515 if( pTos->iFlags & MEMOBJ_REAL ){
2516 pTos->x.rVal--;
2517 /* Try to get an integer representation */
2518 jx9MemObjTryInteger(pTos);
2519 }else{
2520 pTos->x.iVal--;
2521 MemObjSetType(pTos, MEMOBJ_INT);
2522 }
2523 }
2524 }
2525 }
2526 break;
2527/*
2528 * UMINUS: * * *
2529 *
2530 * Perform a unary minus operation.
2531 */
2532case JX9_OP_UMINUS:
2533#ifdef UNTRUST
2534 if( pTos < pStack ){
2535 goto Abort;
2536 }
2537#endif
2538 /* Force a numeric (integer, real or both) cast */
2539 jx9MemObjToNumeric(pTos);
2540 if( pTos->iFlags & MEMOBJ_REAL ){
2541 pTos->x.rVal = -pTos->x.rVal;
2542 }
2543 if( pTos->iFlags & MEMOBJ_INT ){
2544 pTos->x.iVal = -pTos->x.iVal;
2545 }
2546 break;
2547/*
2548 * UPLUS: * * *
2549 *
2550 * Perform a unary plus operation.
2551 */
2552case JX9_OP_UPLUS:
2553#ifdef UNTRUST
2554 if( pTos < pStack ){
2555 goto Abort;
2556 }
2557#endif
2558 /* Force a numeric (integer, real or both) cast */
2559 jx9MemObjToNumeric(pTos);
2560 if( pTos->iFlags & MEMOBJ_REAL ){
2561 pTos->x.rVal = +pTos->x.rVal;
2562 }
2563 if( pTos->iFlags & MEMOBJ_INT ){
2564 pTos->x.iVal = +pTos->x.iVal;
2565 }
2566 break;
2567/*
2568 * OP_LNOT: * * *
2569 *
2570 * Interpret the top of the stack as a boolean value. Replace it
2571 * with its complement.
2572 */
2573case JX9_OP_LNOT:
2574#ifdef UNTRUST
2575 if( pTos < pStack ){
2576 goto Abort;
2577 }
2578#endif
2579 /* Force a boolean cast */
2580 if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
2581 jx9MemObjToBool(pTos);
2582 }
2583 pTos->x.iVal = !pTos->x.iVal;
2584 break;
2585/*
2586 * OP_BITNOT: * * *
2587 *
2588 * Interpret the top of the stack as an value.Replace it
2589 * with its ones-complement.
2590 */
2591case JX9_OP_BITNOT:
2592#ifdef UNTRUST
2593 if( pTos < pStack ){
2594 goto Abort;
2595 }
2596#endif
2597 /* Force an integer cast */
2598 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
2599 jx9MemObjToInteger(pTos);
2600 }
2601 pTos->x.iVal = ~pTos->x.iVal;
2602 break;
2603/* OP_MUL * * *
2604 * OP_MUL_STORE * * *
2605 *
2606 * Pop the top two elements from the stack, multiply them together,
2607 * and push the result back onto the stack.
2608 */
2609case JX9_OP_MUL:
2610case JX9_OP_MUL_STORE: {
2611 jx9_value *pNos = &pTos[-1];
2612 /* Force the operand to be numeric */
2613#ifdef UNTRUST
2614 if( pNos < pStack ){
2615 goto Abort;
2616 }
2617#endif
2618 jx9MemObjToNumeric(pTos);
2619 jx9MemObjToNumeric(pNos);
2620 /* Perform the requested operation */
2621 if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
2622 /* Floating point arithemic */
2623 jx9_real a, b, r;
2624 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
2625 jx9MemObjToReal(pTos);
2626 }
2627 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
2628 jx9MemObjToReal(pNos);
2629 }
2630 a = pNos->x.rVal;
2631 b = pTos->x.rVal;
2632 r = a * b;
2633 /* Push the result */
2634 pNos->x.rVal = r;
2635 MemObjSetType(pNos, MEMOBJ_REAL);
2636 /* Try to get an integer representation */
2637 jx9MemObjTryInteger(pNos);
2638 }else{
2639 /* Integer arithmetic */
2640 sxi64 a, b, r;
2641 a = pNos->x.iVal;
2642 b = pTos->x.iVal;
2643 r = a * b;
2644 /* Push the result */
2645 pNos->x.iVal = r;
2646 MemObjSetType(pNos, MEMOBJ_INT);
2647 }
2648 if( pInstr->iOp == JX9_OP_MUL_STORE ){
2649 jx9_value *pObj;
2650 if( pTos->nIdx == SXU32_HIGH ){
2651 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
2652 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
2653 jx9MemObjStore(pNos, pObj);
2654 }
2655 }
2656 VmPopOperand(&pTos, 1);
2657 break;
2658 }
2659/* OP_ADD * * *
2660 *
2661 * Pop the top two elements from the stack, add them together,
2662 * and push the result back onto the stack.
2663 */
2664case JX9_OP_ADD:{
2665 jx9_value *pNos = &pTos[-1];
2666#ifdef UNTRUST
2667 if( pNos < pStack ){
2668 goto Abort;
2669 }
2670#endif
2671 /* Perform the addition */
2672 jx9MemObjAdd(pNos, pTos, FALSE);
2673 VmPopOperand(&pTos, 1);
2674 break;
2675 }
2676/*
2677 * OP_ADD_STORE * * *
2678 *
2679 * Pop the top two elements from the stack, add them together,
2680 * and push the result back onto the stack.
2681 */
2682case JX9_OP_ADD_STORE:{
2683 jx9_value *pNos = &pTos[-1];
2684 jx9_value *pObj;
2685 sxu32 nIdx;
2686#ifdef UNTRUST
2687 if( pNos < pStack ){
2688 goto Abort;
2689 }
2690#endif
2691 /* Perform the addition */
2692 nIdx = pTos->nIdx;
2693 jx9MemObjAdd(pTos, pNos, TRUE);
2694 /* Peform the store operation */
2695 if( nIdx == SXU32_HIGH ){
2696 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
2697 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx)) != 0 ){
2698 jx9MemObjStore(pTos, pObj);
2699 }
2700 /* Ticket 1433-35: Perform a stack dup */
2701 jx9MemObjStore(pTos, pNos);
2702 VmPopOperand(&pTos, 1);
2703 break;
2704 }
2705/* OP_SUB * * *
2706 *
2707 * Pop the top two elements from the stack, subtract the
2708 * first (what was next on the stack) from the second (the
2709 * top of the stack) and push the result back onto the stack.
2710 */
2711case JX9_OP_SUB: {
2712 jx9_value *pNos = &pTos[-1];
2713#ifdef UNTRUST
2714 if( pNos < pStack ){
2715 goto Abort;
2716 }
2717#endif
2718 if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
2719 /* Floating point arithemic */
2720 jx9_real a, b, r;
2721 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
2722 jx9MemObjToReal(pTos);
2723 }
2724 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
2725 jx9MemObjToReal(pNos);
2726 }
2727 a = pNos->x.rVal;
2728 b = pTos->x.rVal;
2729 r = a - b;
2730 /* Push the result */
2731 pNos->x.rVal = r;
2732 MemObjSetType(pNos, MEMOBJ_REAL);
2733 /* Try to get an integer representation */
2734 jx9MemObjTryInteger(pNos);
2735 }else{
2736 /* Integer arithmetic */
2737 sxi64 a, b, r;
2738 a = pNos->x.iVal;
2739 b = pTos->x.iVal;
2740 r = a - b;
2741 /* Push the result */
2742 pNos->x.iVal = r;
2743 MemObjSetType(pNos, MEMOBJ_INT);
2744 }
2745 VmPopOperand(&pTos, 1);
2746 break;
2747 }
2748/* OP_SUB_STORE * * *
2749 *
2750 * Pop the top two elements from the stack, subtract the
2751 * first (what was next on the stack) from the second (the
2752 * top of the stack) and push the result back onto the stack.
2753 */
2754case JX9_OP_SUB_STORE: {
2755 jx9_value *pNos = &pTos[-1];
2756 jx9_value *pObj;
2757#ifdef UNTRUST
2758 if( pNos < pStack ){
2759 goto Abort;
2760 }
2761#endif
2762 if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
2763 /* Floating point arithemic */
2764 jx9_real a, b, r;
2765 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
2766 jx9MemObjToReal(pTos);
2767 }
2768 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
2769 jx9MemObjToReal(pNos);
2770 }
2771 a = pTos->x.rVal;
2772 b = pNos->x.rVal;
2773 r = a - b;
2774 /* Push the result */
2775 pNos->x.rVal = r;
2776 MemObjSetType(pNos, MEMOBJ_REAL);
2777 /* Try to get an integer representation */
2778 jx9MemObjTryInteger(pNos);
2779 }else{
2780 /* Integer arithmetic */
2781 sxi64 a, b, r;
2782 a = pTos->x.iVal;
2783 b = pNos->x.iVal;
2784 r = a - b;
2785 /* Push the result */
2786 pNos->x.iVal = r;
2787 MemObjSetType(pNos, MEMOBJ_INT);
2788 }
2789 if( pTos->nIdx == SXU32_HIGH ){
2790 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
2791 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
2792 jx9MemObjStore(pNos, pObj);
2793 }
2794 VmPopOperand(&pTos, 1);
2795 break;
2796 }
2797
2798/*
2799 * OP_MOD * * *
2800 *
2801 * Pop the top two elements from the stack, divide the
2802 * first (what was next on the stack) from the second (the
2803 * top of the stack) and push the remainder after division
2804 * onto the stack.
2805 * Note: Only integer arithemtic is allowed.
2806 */
2807case JX9_OP_MOD:{
2808 jx9_value *pNos = &pTos[-1];
2809 sxi64 a, b, r;
2810#ifdef UNTRUST
2811 if( pNos < pStack ){
2812 goto Abort;
2813 }
2814#endif
2815 /* Force the operands to be integer */
2816 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
2817 jx9MemObjToInteger(pTos);
2818 }
2819 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
2820 jx9MemObjToInteger(pNos);
2821 }
2822 /* Perform the requested operation */
2823 a = pNos->x.iVal;
2824 b = pTos->x.iVal;
2825 if( b == 0 ){
2826 r = 0;
2827 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
2828 /* goto Abort; */
2829 }else{
2830 r = a%b;
2831 }
2832 /* Push the result */
2833 pNos->x.iVal = r;
2834 MemObjSetType(pNos, MEMOBJ_INT);
2835 VmPopOperand(&pTos, 1);
2836 break;
2837 }
2838/*
2839 * OP_MOD_STORE * * *
2840 *
2841 * Pop the top two elements from the stack, divide the
2842 * first (what was next on the stack) from the second (the
2843 * top of the stack) and push the remainder after division
2844 * onto the stack.
2845 * Note: Only integer arithemtic is allowed.
2846 */
2847case JX9_OP_MOD_STORE: {
2848 jx9_value *pNos = &pTos[-1];
2849 jx9_value *pObj;
2850 sxi64 a, b, r;
2851#ifdef UNTRUST
2852 if( pNos < pStack ){
2853 goto Abort;
2854 }
2855#endif
2856 /* Force the operands to be integer */
2857 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
2858 jx9MemObjToInteger(pTos);
2859 }
2860 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
2861 jx9MemObjToInteger(pNos);
2862 }
2863 /* Perform the requested operation */
2864 a = pTos->x.iVal;
2865 b = pNos->x.iVal;
2866 if( b == 0 ){
2867 r = 0;
2868 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
2869 /* goto Abort; */
2870 }else{
2871 r = a%b;
2872 }
2873 /* Push the result */
2874 pNos->x.iVal = r;
2875 MemObjSetType(pNos, MEMOBJ_INT);
2876 if( pTos->nIdx == SXU32_HIGH ){
2877 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
2878 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
2879 jx9MemObjStore(pNos, pObj);
2880 }
2881 VmPopOperand(&pTos, 1);
2882 break;
2883 }
2884/*
2885 * OP_DIV * * *
2886 *
2887 * Pop the top two elements from the stack, divide the
2888 * first (what was next on the stack) from the second (the
2889 * top of the stack) and push the result onto the stack.
2890 * Note: Only floating point arithemtic is allowed.
2891 */
2892case JX9_OP_DIV:{
2893 jx9_value *pNos = &pTos[-1];
2894 jx9_real a, b, r;
2895#ifdef UNTRUST
2896 if( pNos < pStack ){
2897 goto Abort;
2898 }
2899#endif
2900 /* Force the operands to be real */
2901 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
2902 jx9MemObjToReal(pTos);
2903 }
2904 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
2905 jx9MemObjToReal(pNos);
2906 }
2907 /* Perform the requested operation */
2908 a = pNos->x.rVal;
2909 b = pTos->x.rVal;
2910 if( b == 0 ){
2911 /* Division by zero */
2912 r = 0;
2913 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Division by zero");
2914 /* goto Abort; */
2915 }else{
2916 r = a/b;
2917 /* Push the result */
2918 pNos->x.rVal = r;
2919 MemObjSetType(pNos, MEMOBJ_REAL);
2920 /* Try to get an integer representation */
2921 jx9MemObjTryInteger(pNos);
2922 }
2923 VmPopOperand(&pTos, 1);
2924 break;
2925 }
2926/*
2927 * OP_DIV_STORE * * *
2928 *
2929 * Pop the top two elements from the stack, divide the
2930 * first (what was next on the stack) from the second (the
2931 * top of the stack) and push the result onto the stack.
2932 * Note: Only floating point arithemtic is allowed.
2933 */
2934case JX9_OP_DIV_STORE:{
2935 jx9_value *pNos = &pTos[-1];
2936 jx9_value *pObj;
2937 jx9_real a, b, r;
2938#ifdef UNTRUST
2939 if( pNos < pStack ){
2940 goto Abort;
2941 }
2942#endif
2943 /* Force the operands to be real */
2944 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
2945 jx9MemObjToReal(pTos);
2946 }
2947 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
2948 jx9MemObjToReal(pNos);
2949 }
2950 /* Perform the requested operation */
2951 a = pTos->x.rVal;
2952 b = pNos->x.rVal;
2953 if( b == 0 ){
2954 /* Division by zero */
2955 r = 0;
2956 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd/0", a);
2957 /* goto Abort; */
2958 }else{
2959 r = a/b;
2960 /* Push the result */
2961 pNos->x.rVal = r;
2962 MemObjSetType(pNos, MEMOBJ_REAL);
2963 /* Try to get an integer representation */
2964 jx9MemObjTryInteger(pNos);
2965 }
2966 if( pTos->nIdx == SXU32_HIGH ){
2967 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
2968 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
2969 jx9MemObjStore(pNos, pObj);
2970 }
2971 VmPopOperand(&pTos, 1);
2972 break;
2973 }
2974/* OP_BAND * * *
2975 *
2976 * Pop the top two elements from the stack. Convert both elements
2977 * to integers. Push back onto the stack the bit-wise AND of the
2978 * two elements.
2979*/
2980/* OP_BOR * * *
2981 *
2982 * Pop the top two elements from the stack. Convert both elements
2983 * to integers. Push back onto the stack the bit-wise OR of the
2984 * two elements.
2985 */
2986/* OP_BXOR * * *
2987 *
2988 * Pop the top two elements from the stack. Convert both elements
2989 * to integers. Push back onto the stack the bit-wise XOR of the
2990 * two elements.
2991 */
2992case JX9_OP_BAND:
2993case JX9_OP_BOR:
2994case JX9_OP_BXOR:{
2995 jx9_value *pNos = &pTos[-1];
2996 sxi64 a, b, r;
2997#ifdef UNTRUST
2998 if( pNos < pStack ){
2999 goto Abort;
3000 }
3001#endif
3002 /* Force the operands to be integer */
3003 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
3004 jx9MemObjToInteger(pTos);
3005 }
3006 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
3007 jx9MemObjToInteger(pNos);
3008 }
3009 /* Perform the requested operation */
3010 a = pNos->x.iVal;
3011 b = pTos->x.iVal;
3012 switch(pInstr->iOp){
3013 case JX9_OP_BOR_STORE:
3014 case JX9_OP_BOR: r = a|b; break;
3015 case JX9_OP_BXOR_STORE:
3016 case JX9_OP_BXOR: r = a^b; break;
3017 case JX9_OP_BAND_STORE:
3018 case JX9_OP_BAND:
3019 default: r = a&b; break;
3020 }
3021 /* Push the result */
3022 pNos->x.iVal = r;
3023 MemObjSetType(pNos, MEMOBJ_INT);
3024 VmPopOperand(&pTos, 1);
3025 break;
3026 }
3027/* OP_BAND_STORE * * *
3028 *
3029 * Pop the top two elements from the stack. Convert both elements
3030 * to integers. Push back onto the stack the bit-wise AND of the
3031 * two elements.
3032*/
3033/* OP_BOR_STORE * * *
3034 *
3035 * Pop the top two elements from the stack. Convert both elements
3036 * to integers. Push back onto the stack the bit-wise OR of the
3037 * two elements.
3038 */
3039/* OP_BXOR_STORE * * *
3040 *
3041 * Pop the top two elements from the stack. Convert both elements
3042 * to integers. Push back onto the stack the bit-wise XOR of the
3043 * two elements.
3044 */
3045case JX9_OP_BAND_STORE:
3046case JX9_OP_BOR_STORE:
3047case JX9_OP_BXOR_STORE:{
3048 jx9_value *pNos = &pTos[-1];
3049 jx9_value *pObj;
3050 sxi64 a, b, r;
3051#ifdef UNTRUST
3052 if( pNos < pStack ){
3053 goto Abort;
3054 }
3055#endif
3056 /* Force the operands to be integer */
3057 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
3058 jx9MemObjToInteger(pTos);
3059 }
3060 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
3061 jx9MemObjToInteger(pNos);
3062 }
3063 /* Perform the requested operation */
3064 a = pTos->x.iVal;
3065 b = pNos->x.iVal;
3066 switch(pInstr->iOp){
3067 case JX9_OP_BOR_STORE:
3068 case JX9_OP_BOR: r = a|b; break;
3069 case JX9_OP_BXOR_STORE:
3070 case JX9_OP_BXOR: r = a^b; break;
3071 case JX9_OP_BAND_STORE:
3072 case JX9_OP_BAND:
3073 default: r = a&b; break;
3074 }
3075 /* Push the result */
3076 pNos->x.iVal = r;
3077 MemObjSetType(pNos, MEMOBJ_INT);
3078 if( pTos->nIdx == SXU32_HIGH ){
3079 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
3080 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
3081 jx9MemObjStore(pNos, pObj);
3082 }
3083 VmPopOperand(&pTos, 1);
3084 break;
3085 }
3086/* OP_SHL * * *
3087 *
3088 * Pop the top two elements from the stack. Convert both elements
3089 * to integers. Push back onto the stack the second element shifted
3090 * left by N bits where N is the top element on the stack.
3091 * Note: Only integer arithmetic is allowed.
3092 */
3093/* OP_SHR * * *
3094 *
3095 * Pop the top two elements from the stack. Convert both elements
3096 * to integers. Push back onto the stack the second element shifted
3097 * right by N bits where N is the top element on the stack.
3098 * Note: Only integer arithmetic is allowed.
3099 */
3100case JX9_OP_SHL:
3101case JX9_OP_SHR: {
3102 jx9_value *pNos = &pTos[-1];
3103 sxi64 a, r;
3104 sxi32 b;
3105#ifdef UNTRUST
3106 if( pNos < pStack ){
3107 goto Abort;
3108 }
3109#endif
3110 /* Force the operands to be integer */
3111 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
3112 jx9MemObjToInteger(pTos);
3113 }
3114 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
3115 jx9MemObjToInteger(pNos);
3116 }
3117 /* Perform the requested operation */
3118 a = pNos->x.iVal;
3119 b = (sxi32)pTos->x.iVal;
3120 if( pInstr->iOp == JX9_OP_SHL ){
3121 r = a << b;
3122 }else{
3123 r = a >> b;
3124 }
3125 /* Push the result */
3126 pNos->x.iVal = r;
3127 MemObjSetType(pNos, MEMOBJ_INT);
3128 VmPopOperand(&pTos, 1);
3129 break;
3130 }
3131/* OP_SHL_STORE * * *
3132 *
3133 * Pop the top two elements from the stack. Convert both elements
3134 * to integers. Push back onto the stack the second element shifted
3135 * left by N bits where N is the top element on the stack.
3136 * Note: Only integer arithmetic is allowed.
3137 */
3138/* OP_SHR_STORE * * *
3139 *
3140 * Pop the top two elements from the stack. Convert both elements
3141 * to integers. Push back onto the stack the second element shifted
3142 * right by N bits where N is the top element on the stack.
3143 * Note: Only integer arithmetic is allowed.
3144 */
3145case JX9_OP_SHL_STORE:
3146case JX9_OP_SHR_STORE: {
3147 jx9_value *pNos = &pTos[-1];
3148 jx9_value *pObj;
3149 sxi64 a, r;
3150 sxi32 b;
3151#ifdef UNTRUST
3152 if( pNos < pStack ){
3153 goto Abort;
3154 }
3155#endif
3156 /* Force the operands to be integer */
3157 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
3158 jx9MemObjToInteger(pTos);
3159 }
3160 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
3161 jx9MemObjToInteger(pNos);
3162 }
3163 /* Perform the requested operation */
3164 a = pTos->x.iVal;
3165 b = (sxi32)pNos->x.iVal;
3166 if( pInstr->iOp == JX9_OP_SHL_STORE ){
3167 r = a << b;
3168 }else{
3169 r = a >> b;
3170 }
3171 /* Push the result */
3172 pNos->x.iVal = r;
3173 MemObjSetType(pNos, MEMOBJ_INT);
3174 if( pTos->nIdx == SXU32_HIGH ){
3175 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
3176 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
3177 jx9MemObjStore(pNos, pObj);
3178 }
3179 VmPopOperand(&pTos, 1);
3180 break;
3181 }
3182/* CAT: P1 * *
3183 *
3184 * Pop P1 elements from the stack. Concatenate them togeher and push the result
3185 * back.
3186 */
3187case JX9_OP_CAT:{
3188 jx9_value *pNos, *pCur;
3189 if( pInstr->iP1 < 1 ){
3190 pNos = &pTos[-1];
3191 }else{
3192 pNos = &pTos[-pInstr->iP1+1];
3193 }
3194#ifdef UNTRUST
3195 if( pNos < pStack ){
3196 goto Abort;
3197 }
3198#endif
3199 /* Force a string cast */
3200 if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){
3201 jx9MemObjToString(pNos);
3202 }
3203 pCur = &pNos[1];
3204 while( pCur <= pTos ){
3205 if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){
3206 jx9MemObjToString(pCur);
3207 }
3208 /* Perform the concatenation */
3209 if( SyBlobLength(&pCur->sBlob) > 0 ){
3210 jx9MemObjStringAppend(pNos, (const char *)SyBlobData(&pCur->sBlob), SyBlobLength(&pCur->sBlob));
3211 }
3212 SyBlobRelease(&pCur->sBlob);
3213 pCur++;
3214 }
3215 pTos = pNos;
3216 break;
3217 }
3218/* CAT_STORE: * * *
3219 *
3220 * Pop two elements from the stack. Concatenate them togeher and push the result
3221 * back.
3222 */
3223case JX9_OP_CAT_STORE:{
3224 jx9_value *pNos = &pTos[-1];
3225 jx9_value *pObj;
3226#ifdef UNTRUST
3227 if( pNos < pStack ){
3228 goto Abort;
3229 }
3230#endif
3231 if((pTos->iFlags & MEMOBJ_STRING) == 0 ){
3232 /* Force a string cast */
3233 jx9MemObjToString(pTos);
3234 }
3235 if((pNos->iFlags & MEMOBJ_STRING) == 0 ){
3236 /* Force a string cast */
3237 jx9MemObjToString(pNos);
3238 }
3239 /* Perform the concatenation (Reverse order) */
3240 if( SyBlobLength(&pNos->sBlob) > 0 ){
3241 jx9MemObjStringAppend(pTos, (const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob));
3242 }
3243 /* Perform the store operation */
3244 if( pTos->nIdx == SXU32_HIGH ){
3245 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
3246 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
3247 jx9MemObjStore(pTos, pObj);
3248 }
3249 jx9MemObjStore(pTos, pNos);
3250 VmPopOperand(&pTos, 1);
3251 break;
3252 }
3253/* OP_AND: * * *
3254 *
3255 * Pop two values off the stack. Take the logical AND of the
3256 * two values and push the resulting boolean value back onto the
3257 * stack.
3258 */
3259/* OP_OR: * * *
3260 *
3261 * Pop two values off the stack. Take the logical OR of the
3262 * two values and push the resulting boolean value back onto the
3263 * stack.
3264 */
3265case JX9_OP_LAND:
3266case JX9_OP_LOR: {
3267 jx9_value *pNos = &pTos[-1];
3268 sxi32 v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */
3269#ifdef UNTRUST
3270 if( pNos < pStack ){
3271 goto Abort;
3272 }
3273#endif
3274 /* Force a boolean cast */
3275 if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
3276 jx9MemObjToBool(pTos);
3277 }
3278 if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
3279 jx9MemObjToBool(pNos);
3280 }
3281 v1 = pNos->x.iVal == 0 ? 1 : 0;
3282 v2 = pTos->x.iVal == 0 ? 1 : 0;
3283 if( pInstr->iOp == JX9_OP_LAND ){
3284 static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
3285 v1 = and_logic[v1*3+v2];
3286 }else{
3287 static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
3288 v1 = or_logic[v1*3+v2];
3289 }
3290 if( v1 == 2 ){
3291 v1 = 1;
3292 }
3293 VmPopOperand(&pTos, 1);
3294 pTos->x.iVal = v1 == 0 ? 1 : 0;
3295 MemObjSetType(pTos, MEMOBJ_BOOL);
3296 break;
3297 }
3298/* OP_LXOR: * * *
3299 *
3300 * Pop two values off the stack. Take the logical XOR of the
3301 * two values and push the resulting boolean value back onto the
3302 * stack.
3303 * According to the JX9 language reference manual:
3304 * $a xor $b is evaluated to TRUE if either $a or $b is
3305 * TRUE, but not both.
3306 */
3307case JX9_OP_LXOR:{
3308 jx9_value *pNos = &pTos[-1];
3309 sxi32 v = 0;
3310#ifdef UNTRUST
3311 if( pNos < pStack ){
3312 goto Abort;
3313 }
3314#endif
3315 /* Force a boolean cast */
3316 if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
3317 jx9MemObjToBool(pTos);
3318 }
3319 if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
3320 jx9MemObjToBool(pNos);
3321 }
3322 if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){
3323 v = 1;
3324 }
3325 VmPopOperand(&pTos, 1);
3326 pTos->x.iVal = v;
3327 MemObjSetType(pTos, MEMOBJ_BOOL);
3328 break;
3329 }
3330/* OP_EQ P1 P2 P3
3331 *
3332 * Pop the top two elements from the stack. If they are equal, then
3333 * jump to instruction P2. Otherwise, continue to the next instruction.
3334 * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
3335 * stack if the jump would have been taken, or a 0 (FALSE) if not.
3336 */
3337/* OP_NEQ P1 P2 P3
3338 *
3339 * Pop the top two elements from the stack. If they are not equal, then
3340 * jump to instruction P2. Otherwise, continue to the next instruction.
3341 * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
3342 * stack if the jump would have been taken, or a 0 (FALSE) if not.
3343 */
3344case JX9_OP_EQ:
3345case JX9_OP_NEQ: {
3346 jx9_value *pNos = &pTos[-1];
3347 /* Perform the comparison and act accordingly */
3348#ifdef UNTRUST
3349 if( pNos < pStack ){
3350 goto Abort;
3351 }
3352#endif
3353 rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
3354 if( pInstr->iOp == JX9_OP_EQ ){
3355 rc = rc == 0;
3356 }else{
3357 rc = rc != 0;
3358 }
3359 VmPopOperand(&pTos, 1);
3360 if( !pInstr->iP2 ){
3361 /* Push comparison result without taking the jump */
3362 jx9MemObjRelease(pTos);
3363 pTos->x.iVal = rc;
3364 /* Invalidate any prior representation */
3365 MemObjSetType(pTos, MEMOBJ_BOOL);
3366 }else{
3367 if( rc ){
3368 /* Jump to the desired location */
3369 pc = pInstr->iP2 - 1;
3370 VmPopOperand(&pTos, 1);
3371 }
3372 }
3373 break;
3374 }
3375/* OP_TEQ P1 P2 *
3376 *
3377 * Pop the top two elements from the stack. If they have the same type and are equal
3378 * then jump to instruction P2. Otherwise, continue to the next instruction.
3379 * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
3380 * stack if the jump would have been taken, or a 0 (FALSE) if not.
3381 */
3382case JX9_OP_TEQ: {
3383 jx9_value *pNos = &pTos[-1];
3384 /* Perform the comparison and act accordingly */
3385#ifdef UNTRUST
3386 if( pNos < pStack ){
3387 goto Abort;
3388 }
3389#endif
3390 rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) == 0;
3391 VmPopOperand(&pTos, 1);
3392 if( !pInstr->iP2 ){
3393 /* Push comparison result without taking the jump */
3394 jx9MemObjRelease(pTos);
3395 pTos->x.iVal = rc;
3396 /* Invalidate any prior representation */
3397 MemObjSetType(pTos, MEMOBJ_BOOL);
3398 }else{
3399 if( rc ){
3400 /* Jump to the desired location */
3401 pc = pInstr->iP2 - 1;
3402 VmPopOperand(&pTos, 1);
3403 }
3404 }
3405 break;
3406 }
3407/* OP_TNE P1 P2 *
3408 *
3409 * Pop the top two elements from the stack.If they are not equal an they are not
3410 * of the same type, then jump to instruction P2. Otherwise, continue to the next
3411 * instruction.
3412 * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
3413 * stack if the jump would have been taken, or a 0 (FALSE) if not.
3414 *
3415 */
3416case JX9_OP_TNE: {
3417 jx9_value *pNos = &pTos[-1];
3418 /* Perform the comparison and act accordingly */
3419#ifdef UNTRUST
3420 if( pNos < pStack ){
3421 goto Abort;
3422 }
3423#endif
3424 rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) != 0;
3425 VmPopOperand(&pTos, 1);
3426 if( !pInstr->iP2 ){
3427 /* Push comparison result without taking the jump */
3428 jx9MemObjRelease(pTos);
3429 pTos->x.iVal = rc;
3430 /* Invalidate any prior representation */
3431 MemObjSetType(pTos, MEMOBJ_BOOL);
3432 }else{
3433 if( rc ){
3434 /* Jump to the desired location */
3435 pc = pInstr->iP2 - 1;
3436 VmPopOperand(&pTos, 1);
3437 }
3438 }
3439 break;
3440 }
3441/* OP_LT P1 P2 P3
3442 *
3443 * Pop the top two elements from the stack. If the second element (the top of stack)
3444 * is less than the first (next on stack), then jump to instruction P2.Otherwise
3445 * continue to the next instruction. In other words, jump if pNos<pTos.
3446 * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
3447 * stack if the jump would have been taken, or a 0 (FALSE) if not.
3448 *
3449 */
3450/* OP_LE P1 P2 P3
3451 *
3452 * Pop the top two elements from the stack. If the second element (the top of stack)
3453 * is less than or equal to the first (next on stack), then jump to instruction P2.
3454 * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
3455 * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
3456 * stack if the jump would have been taken, or a 0 (FALSE) if not.
3457 *
3458 */
3459case JX9_OP_LT:
3460case JX9_OP_LE: {
3461 jx9_value *pNos = &pTos[-1];
3462 /* Perform the comparison and act accordingly */
3463#ifdef UNTRUST
3464 if( pNos < pStack ){
3465 goto Abort;
3466 }
3467#endif
3468 rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
3469 if( pInstr->iOp == JX9_OP_LE ){
3470 rc = rc < 1;
3471 }else{
3472 rc = rc < 0;
3473 }
3474 VmPopOperand(&pTos, 1);
3475 if( !pInstr->iP2 ){
3476 /* Push comparison result without taking the jump */
3477 jx9MemObjRelease(pTos);
3478 pTos->x.iVal = rc;
3479 /* Invalidate any prior representation */
3480 MemObjSetType(pTos, MEMOBJ_BOOL);
3481 }else{
3482 if( rc ){
3483 /* Jump to the desired location */
3484 pc = pInstr->iP2 - 1;
3485 VmPopOperand(&pTos, 1);
3486 }
3487 }
3488 break;
3489 }
3490/* OP_GT P1 P2 P3
3491 *
3492 * Pop the top two elements from the stack. If the second element (the top of stack)
3493 * is greater than the first (next on stack), then jump to instruction P2.Otherwise
3494 * continue to the next instruction. In other words, jump if pNos<pTos.
3495 * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
3496 * stack if the jump would have been taken, or a 0 (FALSE) if not.
3497 *
3498 */
3499/* OP_GE P1 P2 P3
3500 *
3501 * Pop the top two elements from the stack. If the second element (the top of stack)
3502 * is greater than or equal to the first (next on stack), then jump to instruction P2.
3503 * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
3504 * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
3505 * stack if the jump would have been taken, or a 0 (FALSE) if not.
3506 *
3507 */
3508case JX9_OP_GT:
3509case JX9_OP_GE: {
3510 jx9_value *pNos = &pTos[-1];
3511 /* Perform the comparison and act accordingly */
3512#ifdef UNTRUST
3513 if( pNos < pStack ){
3514 goto Abort;
3515 }
3516#endif
3517 rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
3518 if( pInstr->iOp == JX9_OP_GE ){
3519 rc = rc >= 0;
3520 }else{
3521 rc = rc > 0;
3522 }
3523 VmPopOperand(&pTos, 1);
3524 if( !pInstr->iP2 ){
3525 /* Push comparison result without taking the jump */
3526 jx9MemObjRelease(pTos);
3527 pTos->x.iVal = rc;
3528 /* Invalidate any prior representation */
3529 MemObjSetType(pTos, MEMOBJ_BOOL);
3530 }else{
3531 if( rc ){
3532 /* Jump to the desired location */
3533 pc = pInstr->iP2 - 1;
3534 VmPopOperand(&pTos, 1);
3535 }
3536 }
3537 break;
3538 }
3539/*
3540 * OP_FOREACH_INIT * P2 P3
3541 * Prepare a foreach step.
3542 */
3543case JX9_OP_FOREACH_INIT: {
3544 jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
3545 void *pName;
3546#ifdef UNTRUST
3547 if( pTos < pStack ){
3548 goto Abort;
3549 }
3550#endif
3551 if( SyStringLength(&pInfo->sValue) < 1 ){
3552 /* Take the variable name from the top of the stack */
3553 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
3554 /* Force a string cast */
3555 jx9MemObjToString(pTos);
3556 }
3557 /* Duplicate name */
3558 if( SyBlobLength(&pTos->sBlob) > 0 ){
3559 pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
3560 SyStringInitFromBuf(&pInfo->sValue, pName, SyBlobLength(&pTos->sBlob));
3561 }
3562 VmPopOperand(&pTos, 1);
3563 }
3564 if( (pInfo->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){
3565 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
3566 /* Force a string cast */
3567 jx9MemObjToString(pTos);
3568 }
3569 /* Duplicate name */
3570 if( SyBlobLength(&pTos->sBlob) > 0 ){
3571 pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
3572 SyStringInitFromBuf(&pInfo->sKey, pName, SyBlobLength(&pTos->sBlob));
3573 }
3574 VmPopOperand(&pTos, 1);
3575 }
3576 /* Make sure we are dealing with a hashmap [i.e. JSON array or object ]*/
3577 if( (pTos->iFlags & (MEMOBJ_HASHMAP)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){
3578 /* Jump out of the loop */
3579 if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){
3580 jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
3581 "Invalid argument supplied for the foreach statement, expecting JSON array or object instance");
3582 }
3583 pc = pInstr->iP2 - 1;
3584 }else{
3585 jx9_foreach_step *pStep;
3586 pStep = (jx9_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_foreach_step));
3587 if( pStep == 0 ){
3588 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
3589 /* Jump out of the loop */
3590 pc = pInstr->iP2 - 1;
3591 }else{
3592 /* Zero the structure */
3593 SyZero(pStep, sizeof(jx9_foreach_step));
3594 /* Prepare the step */
3595 pStep->iFlags = pInfo->iFlags;
3596 if( pTos->iFlags & MEMOBJ_HASHMAP ){
3597 jx9_hashmap *pMap = (jx9_hashmap *)pTos->x.pOther;
3598 /* Reset the internal loop cursor */
3599 jx9HashmapResetLoopCursor(pMap);
3600 /* Mark the step */
3601 pStep->pMap = pMap;
3602 pMap->iRef++;
3603 }
3604 }
3605 if( SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep) ){
3606 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
3607 SyMemBackendPoolFree(&pVm->sAllocator, pStep);
3608 /* Jump out of the loop */
3609 pc = pInstr->iP2 - 1;
3610 }
3611 }
3612 VmPopOperand(&pTos, 1);
3613 break;
3614 }
3615/*
3616 * OP_FOREACH_STEP * P2 P3
3617 * Perform a foreach step. Jump to P2 at the end of the step.
3618 */
3619case JX9_OP_FOREACH_STEP: {
3620 jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
3621 jx9_foreach_step **apStep, *pStep;
3622 jx9_hashmap_node *pNode;
3623 jx9_hashmap *pMap;
3624 jx9_value *pValue;
3625 /* Peek the last step */
3626 apStep = (jx9_foreach_step **)SySetBasePtr(&pInfo->aStep);
3627 pStep = apStep[SySetUsed(&pInfo->aStep) - 1];
3628 pMap = pStep->pMap;
3629 /* Extract the current node value */
3630 pNode = jx9HashmapGetNextEntry(pMap);
3631 if( pNode == 0 ){
3632 /* No more entry to process */
3633 pc = pInstr->iP2 - 1; /* Jump to this destination */
3634 /* Automatically reset the loop cursor */
3635 jx9HashmapResetLoopCursor(pMap);
3636 /* Cleanup the mess left behind */
3637 SyMemBackendPoolFree(&pVm->sAllocator, pStep);
3638 SySetPop(&pInfo->aStep);
3639 jx9HashmapUnref(pMap);
3640 }else{
3641 if( (pStep->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){
3642 jx9_value *pKey = VmExtractMemObj(&(*pVm), &pInfo->sKey, FALSE, TRUE);
3643 if( pKey ){
3644 jx9HashmapExtractNodeKey(pNode, pKey);
3645 }
3646 }
3647 /* Make a copy of the entry value */
3648 pValue = VmExtractMemObj(&(*pVm), &pInfo->sValue, FALSE, TRUE);
3649 if( pValue ){
3650 jx9HashmapExtractNodeValue(pNode, pValue, TRUE);
3651 }
3652 }
3653 break;
3654 }
3655/*
3656 * OP_MEMBER P1 P2
3657 * Load JSON object entry on the stack.
3658 */
3659case JX9_OP_MEMBER: {
3660 jx9_hashmap_node *pNode = 0; /* cc warning */
3661 jx9_hashmap *pMap = 0;
3662 jx9_value *pIdx;
3663 pIdx = pTos;
3664 pTos--;
3665 rc = SXERR_NOTFOUND; /* Assume the index is invalid */
3666 if( pTos->iFlags & MEMOBJ_HASHMAP ){
3667 /* Point to the hashmap */
3668 pMap = (jx9_hashmap *)pTos->x.pOther;
3669 /* Load the desired entry */
3670 rc = jx9HashmapLookup(pMap, pIdx, &pNode);
3671 }
3672 jx9MemObjRelease(pIdx);
3673 if( rc == SXRET_OK ){
3674 /* Load entry contents */
3675 if( pMap->iRef < 2 ){
3676 /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
3677 * of the entry value, rather than pointing to it.
3678 */
3679 pTos->nIdx = SXU32_HIGH;
3680 jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
3681 }else{
3682 pTos->nIdx = pNode->nValIdx;
3683 jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
3684 jx9HashmapUnref(pMap);
3685 }
3686 }else{
3687 /* No such entry, load NULL */
3688 jx9MemObjRelease(pTos);
3689 pTos->nIdx = SXU32_HIGH;
3690 }
3691 break;
3692 }
3693/*
3694 * OP_SWITCH * * P3
3695 * This is the bytecode implementation of the complex switch() JX9 construct.
3696 */
3697case JX9_OP_SWITCH: {
3698 jx9_switch *pSwitch = (jx9_switch *)pInstr->p3;
3699 jx9_case_expr *aCase, *pCase;
3700 jx9_value sValue, sCaseValue;
3701 sxu32 n, nEntry;
3702#ifdef UNTRUST
3703 if( pSwitch == 0 || pTos < pStack ){
3704 goto Abort;
3705 }
3706#endif
3707 /* Point to the case table */
3708 aCase = (jx9_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr);
3709 nEntry = SySetUsed(&pSwitch->aCaseExpr);
3710 /* Select the appropriate case block to execute */
3711 jx9MemObjInit(pVm, &sValue);
3712 jx9MemObjInit(pVm, &sCaseValue);
3713 for( n = 0 ; n < nEntry ; ++n ){
3714 pCase = &aCase[n];
3715 jx9MemObjLoad(pTos, &sValue);
3716 /* Execute the case expression first */
3717 VmLocalExec(pVm,&pCase->aByteCode, &sCaseValue);
3718 /* Compare the two expression */
3719 rc = jx9MemObjCmp(&sValue, &sCaseValue, FALSE, 0);
3720 jx9MemObjRelease(&sValue);
3721 jx9MemObjRelease(&sCaseValue);
3722 if( rc == 0 ){
3723 /* Value match, jump to this block */
3724 pc = pCase->nStart - 1;
3725 break;
3726 }
3727 }
3728 VmPopOperand(&pTos, 1);
3729 if( n >= nEntry ){
3730 /* No approprite case to execute, jump to the default case */
3731 if( pSwitch->nDefault > 0 ){
3732 pc = pSwitch->nDefault - 1;
3733 }else{
3734 /* No default case, jump out of this switch */
3735 pc = pSwitch->nOut - 1;
3736 }
3737 }
3738 break;
3739 }
3740/*
3741 * OP_UPLINK P1 * *
3742 * Link a variable to the top active VM frame.
3743 * This is used to implement the 'uplink' JX9 construct.
3744 */
3745case JX9_OP_UPLINK: {
3746 if( pVm->pFrame->pParent ){
3747 jx9_value *pLink = &pTos[-pInstr->iP1+1];
3748 SyString sName;
3749 /* Perform the link */
3750 while( pLink <= pTos ){
3751 if((pLink->iFlags & MEMOBJ_STRING) == 0 ){
3752 /* Force a string cast */
3753 jx9MemObjToString(pLink);
3754 }
3755 SyStringInitFromBuf(&sName, SyBlobData(&pLink->sBlob), SyBlobLength(&pLink->sBlob));
3756 if( sName.nByte > 0 ){
3757 VmFrameLink(&(*pVm), &sName);
3758 }
3759 pLink++;
3760 }
3761 }
3762 VmPopOperand(&pTos, pInstr->iP1);
3763 break;
3764 }
3765/*
3766 * OP_CALL P1 * *
3767 * Call a JX9 or a foreign function and push the return value of the called
3768 * function on the stack.
3769 */
3770case JX9_OP_CALL: {
3771 jx9_value *pArg = &pTos[-pInstr->iP1];
3772 SyHashEntry *pEntry;
3773 SyString sName;
3774 /* Extract function name */
3775 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
3776 /* Raise exception: Invalid function name */
3777 VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Invalid function name, JX9 is returning NULL.");
3778 /* Pop given arguments */
3779 if( pInstr->iP1 > 0 ){
3780 VmPopOperand(&pTos, pInstr->iP1);
3781 }
3782 /* Assume a null return value so that the program continue it's execution normally */
3783 jx9MemObjRelease(pTos);
3784 break;
3785 }
3786 SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
3787 /* Check for a compiled function first */
3788 pEntry = SyHashGet(&pVm->hFunction, (const void *)sName.zString, sName.nByte);
3789 if( pEntry ){
3790 jx9_vm_func_arg *aFormalArg;
3791 jx9_value *pFrameStack;
3792 jx9_vm_func *pVmFunc;
3793 VmFrame *pFrame;
3794 jx9_value *pObj;
3795 VmSlot sArg;
3796 sxu32 n;
3797 pVmFunc = (jx9_vm_func *)pEntry->pUserData;
3798 /* Check The recursion limit */
3799 if( pVm->nRecursionDepth > pVm->nMaxDepth ){
3800 VmErrorFormat(&(*pVm), JX9_CTX_ERR,
3801 "Recursion limit reached while invoking user function '%z', JX9 will set a NULL return value",
3802 &pVmFunc->sName);
3803 /* Pop given arguments */
3804 if( pInstr->iP1 > 0 ){
3805 VmPopOperand(&pTos, pInstr->iP1);
3806 }
3807 /* Assume a null return value so that the program continue it's execution normally */
3808 jx9MemObjRelease(pTos);
3809 break;
3810 }
3811 if( pVmFunc->pNextName ){
3812 /* Function is candidate for overloading, select the appropriate function to call */
3813 pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos-pArg));
3814 }
3815 /* Extract the formal argument set */
3816 aFormalArg = (jx9_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs);
3817 /* Create a new VM frame */
3818 rc = VmEnterFrame(&(*pVm),pVmFunc,&pFrame);
3819 if( rc != SXRET_OK ){
3820 /* Raise exception: Out of memory */
3821 VmErrorFormat(&(*pVm), JX9_CTX_ERR,
3822 "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
3823 &pVmFunc->sName);
3824 /* Pop given arguments */
3825 if( pInstr->iP1 > 0 ){
3826 VmPopOperand(&pTos, pInstr->iP1);
3827 }
3828 /* Assume a null return value so that the program continue it's execution normally */
3829 jx9MemObjRelease(pTos);
3830 break;
3831 }
3832 if( SySetUsed(&pVmFunc->aStatic) > 0 ){
3833 jx9_vm_func_static_var *pStatic, *aStatic;
3834 /* Install static variables */
3835 aStatic = (jx9_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic);
3836 for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){
3837 pStatic = &aStatic[n];
3838 if( pStatic->nIdx == SXU32_HIGH ){
3839 /* Initialize the static variables */
3840 pObj = VmReserveMemObj(&(*pVm), &pStatic->nIdx);
3841 if( pObj ){
3842 /* Assume a NULL initialization value */
3843 jx9MemObjInit(&(*pVm), pObj);
3844 if( SySetUsed(&pStatic->aByteCode) > 0 ){
3845 /* Evaluate initialization expression (Any complex expression) */
3846 VmLocalExec(&(*pVm), &pStatic->aByteCode, pObj);
3847 }
3848 pObj->nIdx = pStatic->nIdx;
3849 }else{
3850 continue;
3851 }
3852 }
3853 /* Install in the current frame */
3854 SyHashInsert(&pFrame->hVar, SyStringData(&pStatic->sName), SyStringLength(&pStatic->sName),
3855 SX_INT_TO_PTR(pStatic->nIdx));
3856 }
3857 }
3858 /* Push arguments in the local frame */
3859 n = 0;
3860 while( pArg < pTos ){
3861 if( n < SySetUsed(&pVmFunc->aArgs) ){
3862 if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
3863 /* NULL values are redirected to default arguments */
3864 rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pArg);
3865 if( rc == JX9_ABORT ){
3866 goto Abort;
3867 }
3868 }
3869 /* Make sure the given arguments are of the correct type */
3870 if( aFormalArg[n].nType > 0 ){
3871 if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){
3872 ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
3873 /* Cast to the desired type */
3874 if( xCast ){
3875 xCast(pArg);
3876 }
3877 }
3878 }
3879 /* Pass by value, make a copy of the given argument */
3880 pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
3881 }else{
3882 char zName[32];
3883 SyString sName;
3884 /* Set a dummy name */
3885 sName.nByte = SyBufferFormat(zName, sizeof(zName), "[%u]apArg", n);
3886 sName.zString = zName;
3887 /* Annonymous argument */
3888 pObj = VmExtractMemObj(&(*pVm), &sName, TRUE, TRUE);
3889 }
3890 if( pObj ){
3891 jx9MemObjStore(pArg, pObj);
3892 /* Insert argument index */
3893 sArg.nIdx = pObj->nIdx;
3894 sArg.pUserData = 0;
3895 SySetPut(&pFrame->sArg, (const void *)&sArg);
3896 }
3897 jx9MemObjRelease(pArg);
3898 pArg++;
3899 ++n;
3900 }
3901 /* Process default values */
3902 while( n < SySetUsed(&pVmFunc->aArgs) ){
3903 if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
3904 pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
3905 if( pObj ){
3906 /* Evaluate the default value and extract it's result */
3907 rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pObj);
3908 if( rc == JX9_ABORT ){
3909 goto Abort;
3910 }
3911 /* Insert argument index */
3912 sArg.nIdx = pObj->nIdx;
3913 sArg.pUserData = 0;
3914 SySetPut(&pFrame->sArg, (const void *)&sArg);
3915 /* Make sure the default argument is of the correct type */
3916 if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){
3917 ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
3918 /* Cast to the desired type */
3919 xCast(pObj);
3920 }
3921 }
3922 }
3923 ++n;
3924 }
3925 /* Pop arguments, function name from the operand stack and assume the function
3926 * does not return anything.
3927 */
3928 jx9MemObjRelease(pTos);
3929 pTos = &pTos[-pInstr->iP1];
3930 /* Allocate a new operand stack and evaluate the function body */
3931 pFrameStack = VmNewOperandStack(&(*pVm), SySetUsed(&pVmFunc->aByteCode));
3932 if( pFrameStack == 0 ){
3933 /* Raise exception: Out of memory */
3934 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
3935 &pVmFunc->sName);
3936 if( pInstr->iP1 > 0 ){
3937 VmPopOperand(&pTos, pInstr->iP1);
3938 }
3939 break;
3940 }
3941 /* Increment nesting level */
3942 pVm->nRecursionDepth++;
3943 /* Execute function body */
3944 rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(&pVmFunc->aByteCode), pFrameStack, -1, pTos);
3945 /* Decrement nesting level */
3946 pVm->nRecursionDepth--;
3947 /* Free the operand stack */
3948 SyMemBackendFree(&pVm->sAllocator, pFrameStack);
3949 /* Leave the frame */
3950 VmLeaveFrame(&(*pVm));
3951 if( rc == JX9_ABORT ){
3952 /* Abort processing immeditaley */
3953 goto Abort;
3954 }
3955 }else{
3956 jx9_user_func *pFunc;
3957 jx9_context sCtx;
3958 jx9_value sRet;
3959 /* Look for an installed foreign function */
3960 pEntry = SyHashGet(&pVm->hHostFunction, (const void *)sName.zString, sName.nByte);
3961 if( pEntry == 0 ){
3962 /* Call to undefined function */
3963 VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Call to undefined function '%z', JX9 is returning NULL.", &sName);
3964 /* Pop given arguments */
3965 if( pInstr->iP1 > 0 ){
3966 VmPopOperand(&pTos, pInstr->iP1);
3967 }
3968 /* Assume a null return value so that the program continue it's execution normally */
3969 jx9MemObjRelease(pTos);
3970 break;
3971 }
3972 pFunc = (jx9_user_func *)pEntry->pUserData;
3973 /* Start collecting function arguments */
3974 SySetReset(&aArg);
3975 while( pArg < pTos ){
3976 SySetPut(&aArg, (const void *)&pArg);
3977 pArg++;
3978 }
3979 /* Assume a null return value */
3980 jx9MemObjInit(&(*pVm), &sRet);
3981 /* Init the call context */
3982 VmInitCallContext(&sCtx, &(*pVm), pFunc, &sRet, 0);
3983 /* Call the foreign function */
3984 rc = pFunc->xFunc(&sCtx, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg));
3985 /* Release the call context */
3986 VmReleaseCallContext(&sCtx);
3987 if( rc == JX9_ABORT ){
3988 goto Abort;
3989 }
3990 if( pInstr->iP1 > 0 ){
3991 /* Pop function name and arguments */
3992 VmPopOperand(&pTos, pInstr->iP1);
3993 }
3994 /* Save foreign function return value */
3995 jx9MemObjStore(&sRet, pTos);
3996 jx9MemObjRelease(&sRet);
3997 }
3998 break;
3999 }
4000/*
4001 * OP_CONSUME: P1 * *
4002 * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack.
4003 */
4004case JX9_OP_CONSUME: {
4005 jx9_output_consumer *pCons = &pVm->sVmConsumer;
4006 jx9_value *pCur, *pOut = pTos;
4007
4008 pOut = &pTos[-pInstr->iP1 + 1];
4009 pCur = pOut;
4010 /* Start the consume process */
4011 while( pOut <= pTos ){
4012 /* Force a string cast */
4013 if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){
4014 jx9MemObjToString(pOut);
4015 }
4016 if( SyBlobLength(&pOut->sBlob) > 0 ){
4017 /*SyBlobNullAppend(&pOut->sBlob);*/
4018 /* Invoke the output consumer callback */
4019 rc = pCons->xConsumer(SyBlobData(&pOut->sBlob), SyBlobLength(&pOut->sBlob), pCons->pUserData);
4020 /* Increment output length */
4021 pVm->nOutputLen += SyBlobLength(&pOut->sBlob);
4022 SyBlobRelease(&pOut->sBlob);
4023 if( rc == SXERR_ABORT ){
4024 /* Output consumer callback request an operation abort. */
4025 goto Abort;
4026 }
4027 }
4028 pOut++;
4029 }
4030 pTos = &pCur[-1];
4031 break;
4032 }
4033
4034 } /* Switch() */
4035 pc++; /* Next instruction in the stream */
4036 } /* For(;;) */
4037Done:
4038 SySetRelease(&aArg);
4039 return SXRET_OK;
4040Abort:
4041 SySetRelease(&aArg);
4042 while( pTos >= pStack ){
4043 jx9MemObjRelease(pTos);
4044 pTos--;
4045 }
4046 return JX9_ABORT;
4047}
4048/*
4049 * Execute as much of a local JX9 bytecode program as we can then return.
4050 * This function is a wrapper around [VmByteCodeExec()].
4051 * See block-comment on that function for additional information.
4052 */
4053static sxi32 VmLocalExec(jx9_vm *pVm, SySet *pByteCode,jx9_value *pResult)
4054{
4055 jx9_value *pStack;
4056 sxi32 rc;
4057 /* Allocate a new operand stack */
4058 pStack = VmNewOperandStack(&(*pVm), SySetUsed(pByteCode));
4059 if( pStack == 0 ){
4060 return SXERR_MEM;
4061 }
4062 /* Execute the program */
4063 rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pByteCode), pStack, -1, &(*pResult));
4064 /* Free the operand stack */
4065 SyMemBackendFree(&pVm->sAllocator, pStack);
4066 /* Execution result */
4067 return rc;
4068}
4069/*
4070 * Execute as much of a JX9 bytecode program as we can then return.
4071 * This function is a wrapper around [VmByteCodeExec()].
4072 * See block-comment on that function for additional information.
4073 */
4074JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm)
4075{
4076 /* Make sure we are ready to execute this program */
4077 if( pVm->nMagic != JX9_VM_RUN ){
4078 return pVm->nMagic == JX9_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */
4079 }
4080 /* Set the execution magic number */
4081 pVm->nMagic = JX9_VM_EXEC;
4082 /* Execute the program */
4083 VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pVm->pByteContainer), pVm->aOps, -1, &pVm->sExec);
4084 /*
4085 * TICKET 1433-100: Do not remove the JX9_VM_EXEC magic number
4086 * so that any following call to [jx9_vm_exec()] without calling
4087 * [jx9_vm_reset()] first would fail.
4088 */
4089 return SXRET_OK;
4090}
4091/*
4092 * Extract a memory object (i.e. a variable) from the running script.
4093 * This function must be called after calling jx9_vm_exec(). Otherwise
4094 * NULL is returned.
4095 */
4096JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar)
4097{
4098 jx9_value *pValue;
4099 if( pVm->nMagic != JX9_VM_EXEC ){
4100 /* call jx9_vm_exec() first */
4101 return 0;
4102 }
4103 /* Perform the lookup */
4104 pValue = VmExtractMemObj(pVm,pVar,FALSE,FALSE);
4105 /* Lookup result */
4106 return pValue;
4107}
4108/*
4109 * Invoke the installed VM output consumer callback to consume
4110 * the desired message.
4111 * Refer to the implementation of [jx9_context_output()] defined
4112 * in 'api.c' for additional information.
4113 */
4114JX9_PRIVATE sxi32 jx9VmOutputConsume(
4115 jx9_vm *pVm, /* Target VM */
4116 SyString *pString /* Message to output */
4117 )
4118{
4119 jx9_output_consumer *pCons = &pVm->sVmConsumer;
4120 sxi32 rc = SXRET_OK;
4121 /* Call the output consumer */
4122 if( pString->nByte > 0 ){
4123 rc = pCons->xConsumer((const void *)pString->zString, pString->nByte, pCons->pUserData);
4124 /* Increment output length */
4125 pVm->nOutputLen += pString->nByte;
4126 }
4127 return rc;
4128}
4129/*
4130 * Format a message and invoke the installed VM output consumer
4131 * callback to consume the formatted message.
4132 * Refer to the implementation of [jx9_context_output_format()] defined
4133 * in 'api.c' for additional information.
4134 */
4135JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(
4136 jx9_vm *pVm, /* Target VM */
4137 const char *zFormat, /* Formatted message to output */
4138 va_list ap /* Variable list of arguments */
4139 )
4140{
4141 jx9_output_consumer *pCons = &pVm->sVmConsumer;
4142 sxi32 rc = SXRET_OK;
4143 SyBlob sWorker;
4144 /* Format the message and call the output consumer */
4145 SyBlobInit(&sWorker, &pVm->sAllocator);
4146 SyBlobFormatAp(&sWorker, zFormat, ap);
4147 if( SyBlobLength(&sWorker) > 0 ){
4148 /* Consume the formatted message */
4149 rc = pCons->xConsumer(SyBlobData(&sWorker), SyBlobLength(&sWorker), pCons->pUserData);
4150 }
4151 /* Increment output length */
4152 pVm->nOutputLen += SyBlobLength(&sWorker);
4153 /* Release the working buffer */
4154 SyBlobRelease(&sWorker);
4155 return rc;
4156}
4157/*
4158 * Return a string representation of the given JX9 OP code.
4159 * This function never fail and always return a pointer
4160 * to a null terminated string.
4161 */
4162static const char * VmInstrToString(sxi32 nOp)
4163{
4164 const char *zOp = "Unknown ";
4165 switch(nOp){
4166 case JX9_OP_DONE: zOp = "DONE "; break;
4167 case JX9_OP_HALT: zOp = "HALT "; break;
4168 case JX9_OP_LOAD: zOp = "LOAD "; break;
4169 case JX9_OP_LOADC: zOp = "LOADC "; break;
4170 case JX9_OP_LOAD_MAP: zOp = "LOAD_MAP "; break;
4171 case JX9_OP_LOAD_IDX: zOp = "LOAD_IDX "; break;
4172 case JX9_OP_NOOP: zOp = "NOOP "; break;
4173 case JX9_OP_JMP: zOp = "JMP "; break;
4174 case JX9_OP_JZ: zOp = "JZ "; break;
4175 case JX9_OP_JNZ: zOp = "JNZ "; break;
4176 case JX9_OP_POP: zOp = "POP "; break;
4177 case JX9_OP_CAT: zOp = "CAT "; break;
4178 case JX9_OP_CVT_INT: zOp = "CVT_INT "; break;
4179 case JX9_OP_CVT_STR: zOp = "CVT_STR "; break;
4180 case JX9_OP_CVT_REAL: zOp = "CVT_REAL "; break;
4181 case JX9_OP_CALL: zOp = "CALL "; break;
4182 case JX9_OP_UMINUS: zOp = "UMINUS "; break;
4183 case JX9_OP_UPLUS: zOp = "UPLUS "; break;
4184 case JX9_OP_BITNOT: zOp = "BITNOT "; break;
4185 case JX9_OP_LNOT: zOp = "LOGNOT "; break;
4186 case JX9_OP_MUL: zOp = "MUL "; break;
4187 case JX9_OP_DIV: zOp = "DIV "; break;
4188 case JX9_OP_MOD: zOp = "MOD "; break;
4189 case JX9_OP_ADD: zOp = "ADD "; break;
4190 case JX9_OP_SUB: zOp = "SUB "; break;
4191 case JX9_OP_SHL: zOp = "SHL "; break;
4192 case JX9_OP_SHR: zOp = "SHR "; break;
4193 case JX9_OP_LT: zOp = "LT "; break;
4194 case JX9_OP_LE: zOp = "LE "; break;
4195 case JX9_OP_GT: zOp = "GT "; break;
4196 case JX9_OP_GE: zOp = "GE "; break;
4197 case JX9_OP_EQ: zOp = "EQ "; break;
4198 case JX9_OP_NEQ: zOp = "NEQ "; break;
4199 case JX9_OP_TEQ: zOp = "TEQ "; break;
4200 case JX9_OP_TNE: zOp = "TNE "; break;
4201 case JX9_OP_BAND: zOp = "BITAND "; break;
4202 case JX9_OP_BXOR: zOp = "BITXOR "; break;
4203 case JX9_OP_BOR: zOp = "BITOR "; break;
4204 case JX9_OP_LAND: zOp = "LOGAND "; break;
4205 case JX9_OP_LOR: zOp = "LOGOR "; break;
4206 case JX9_OP_LXOR: zOp = "LOGXOR "; break;
4207 case JX9_OP_STORE: zOp = "STORE "; break;
4208 case JX9_OP_STORE_IDX: zOp = "STORE_IDX "; break;
4209 case JX9_OP_PULL: zOp = "PULL "; break;
4210 case JX9_OP_SWAP: zOp = "SWAP "; break;
4211 case JX9_OP_YIELD: zOp = "YIELD "; break;
4212 case JX9_OP_CVT_BOOL: zOp = "CVT_BOOL "; break;
4213 case JX9_OP_CVT_NULL: zOp = "CVT_NULL "; break;
4214 case JX9_OP_CVT_ARRAY: zOp = "CVT_JSON "; break;
4215 case JX9_OP_CVT_NUMC: zOp = "CVT_NUMC "; break;
4216 case JX9_OP_INCR: zOp = "INCR "; break;
4217 case JX9_OP_DECR: zOp = "DECR "; break;
4218 case JX9_OP_ADD_STORE: zOp = "ADD_STORE "; break;
4219 case JX9_OP_SUB_STORE: zOp = "SUB_STORE "; break;
4220 case JX9_OP_MUL_STORE: zOp = "MUL_STORE "; break;
4221 case JX9_OP_DIV_STORE: zOp = "DIV_STORE "; break;
4222 case JX9_OP_MOD_STORE: zOp = "MOD_STORE "; break;
4223 case JX9_OP_CAT_STORE: zOp = "CAT_STORE "; break;
4224 case JX9_OP_SHL_STORE: zOp = "SHL_STORE "; break;
4225 case JX9_OP_SHR_STORE: zOp = "SHR_STORE "; break;
4226 case JX9_OP_BAND_STORE: zOp = "BAND_STORE "; break;
4227 case JX9_OP_BOR_STORE: zOp = "BOR_STORE "; break;
4228 case JX9_OP_BXOR_STORE: zOp = "BXOR_STORE "; break;
4229 case JX9_OP_CONSUME: zOp = "CONSUME "; break;
4230 case JX9_OP_MEMBER: zOp = "MEMBER "; break;
4231 case JX9_OP_UPLINK: zOp = "UPLINK "; break;
4232 case JX9_OP_SWITCH: zOp = "SWITCH "; break;
4233 case JX9_OP_FOREACH_INIT:
4234 zOp = "4EACH_INIT "; break;
4235 case JX9_OP_FOREACH_STEP:
4236 zOp = "4EACH_STEP "; break;
4237 default:
4238 break;
4239 }
4240 return zOp;
4241}
4242/*
4243 * Dump JX9 bytecodes instructions to a human readable format.
4244 * The xConsumer() callback which is an used defined function
4245 * is responsible of consuming the generated dump.
4246 */
4247JX9_PRIVATE sxi32 jx9VmDump(
4248 jx9_vm *pVm, /* Target VM */
4249 ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */
4250 void *pUserData /* Last argument to xConsumer() */
4251 )
4252{
4253 sxi32 rc;
4254 rc = VmByteCodeDump(pVm->pByteContainer, xConsumer, pUserData);
4255 return rc;
4256}
4257/*
4258 * Default constant expansion callback used by the 'const' statement if used
4259 * outside a object body [i.e: global or function scope].
4260 * Refer to the implementation of [JX9_CompileConstant()] defined
4261 * in 'compile.c' for additional information.
4262 */
4263JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData)
4264{
4265 SySet *pByteCode = (SySet *)pUserData;
4266 /* Evaluate and expand constant value */
4267 VmLocalExec((jx9_vm *)SySetGetUserData(pByteCode), pByteCode, (jx9_value *)pVal);
4268}
4269/*
4270 * Section:
4271 * Function handling functions.
4272 * Authors:
4273 * Symisc Systems, devel@symisc.net.
4274 * Copyright (C) Symisc Systems, http://jx9.symisc.net
4275 * Status:
4276 * Stable.
4277 */
4278/*
4279 * int func_num_args(void)
4280 * Returns the number of arguments passed to the function.
4281 * Parameters
4282 * None.
4283 * Return
4284 * Total number of arguments passed into the current user-defined function
4285 * or -1 if called from the globe scope.
4286 */
4287static int vm_builtin_func_num_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
4288{
4289 VmFrame *pFrame;
4290 jx9_vm *pVm;
4291 /* Point to the target VM */
4292 pVm = pCtx->pVm;
4293 /* Current frame */
4294 pFrame = pVm->pFrame;
4295 if( pFrame->pParent == 0 ){
4296 SXUNUSED(nArg);
4297 SXUNUSED(apArg);
4298 /* Global frame, return -1 */
4299 jx9_result_int(pCtx, -1);
4300 return SXRET_OK;
4301 }
4302 /* Total number of arguments passed to the enclosing function */
4303 nArg = (int)SySetUsed(&pFrame->sArg);
4304 jx9_result_int(pCtx, nArg);
4305 return SXRET_OK;
4306}
4307/*
4308 * value func_get_arg(int $arg_num)
4309 * Return an item from the argument list.
4310 * Parameters
4311 * Argument number(index start from zero).
4312 * Return
4313 * Returns the specified argument or FALSE on error.
4314 */
4315static int vm_builtin_func_get_arg(jx9_context *pCtx, int nArg, jx9_value **apArg)
4316{
4317 jx9_value *pObj = 0;
4318 VmSlot *pSlot = 0;
4319 VmFrame *pFrame;
4320 jx9_vm *pVm;
4321 /* Point to the target VM */
4322 pVm = pCtx->pVm;
4323 /* Current frame */
4324 pFrame = pVm->pFrame;
4325 if( nArg < 1 || pFrame->pParent == 0 ){
4326 /* Global frame or Missing arguments, return FALSE */
4327 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
4328 jx9_result_bool(pCtx, 0);
4329 return SXRET_OK;
4330 }
4331 /* Extract the desired index */
4332 nArg = jx9_value_to_int(apArg[0]);
4333 if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){
4334 /* Invalid index, return FALSE */
4335 jx9_result_bool(pCtx, 0);
4336 return SXRET_OK;
4337 }
4338 /* Extract the desired argument */
4339 if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0 ){
4340 if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0 ){
4341 /* Return the desired argument */
4342 jx9_result_value(pCtx, (jx9_value *)pObj);
4343 }else{
4344 /* No such argument, return false */
4345 jx9_result_bool(pCtx, 0);
4346 }
4347 }else{
4348 /* CAN'T HAPPEN */
4349 jx9_result_bool(pCtx, 0);
4350 }
4351 return SXRET_OK;
4352}
4353/*
4354 * array func_get_args(void)
4355 * Returns an array comprising a copy of function's argument list.
4356 * Parameters
4357 * None.
4358 * Return
4359 * Returns an array in which each element is a copy of the corresponding
4360 * member of the current user-defined function's argument list.
4361 * Otherwise FALSE is returned on failure.
4362 */
4363static int vm_builtin_func_get_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
4364{
4365 jx9_value *pObj = 0;
4366 jx9_value *pArray;
4367 VmFrame *pFrame;
4368 VmSlot *aSlot;
4369 sxu32 n;
4370 /* Point to the current frame */
4371 pFrame = pCtx->pVm->pFrame;
4372 if( pFrame->pParent == 0 ){
4373 /* Global frame, return FALSE */
4374 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
4375 jx9_result_bool(pCtx, 0);
4376 return SXRET_OK;
4377 }
4378 /* Create a new array */
4379 pArray = jx9_context_new_array(pCtx);
4380 if( pArray == 0 ){
4381 SXUNUSED(nArg); /* cc warning */
4382 SXUNUSED(apArg);
4383 jx9_result_bool(pCtx, 0);
4384 return SXRET_OK;
4385 }
4386 /* Start filling the array with the given arguments */
4387 aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg);
4388 for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){
4389 pObj = (jx9_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx);
4390 if( pObj ){
4391 jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pObj);
4392 }
4393 }
4394 /* Return the freshly created array */
4395 jx9_result_value(pCtx, pArray);
4396 return SXRET_OK;
4397}
4398/*
4399 * bool function_exists(string $name)
4400 * Return TRUE if the given function has been defined.
4401 * Parameters
4402 * The name of the desired function.
4403 * Return
4404 * Return TRUE if the given function has been defined.False otherwise
4405 */
4406static int vm_builtin_func_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
4407{
4408 const char *zName;
4409 jx9_vm *pVm;
4410 int nLen;
4411 int res;
4412 if( nArg < 1 ){
4413 /* Missing argument, return FALSE */
4414 jx9_result_bool(pCtx, 0);
4415 return SXRET_OK;
4416 }
4417 /* Point to the target VM */
4418 pVm = pCtx->pVm;
4419 /* Extract the function name */
4420 zName = jx9_value_to_string(apArg[0], &nLen);
4421 /* Assume the function is not defined */
4422 res = 0;
4423 /* Perform the lookup */
4424 if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
4425 SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
4426 /* Function is defined */
4427 res = 1;
4428 }
4429 jx9_result_bool(pCtx, res);
4430 return SXRET_OK;
4431}
4432/*
4433 * Verify that the contents of a variable can be called as a function.
4434 * [i.e: Whether it is callable or not].
4435 * Return TRUE if callable.FALSE otherwise.
4436 */
4437JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue)
4438{
4439 int res = 0;
4440 if( pValue->iFlags & MEMOBJ_STRING ){
4441 const char *zName;
4442 int nLen;
4443 /* Extract the name */
4444 zName = jx9_value_to_string(pValue, &nLen);
4445 /* Perform the lookup */
4446 if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
4447 SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
4448 /* Function is callable */
4449 res = 1;
4450 }
4451 }
4452 return res;
4453}
4454/*
4455 * bool is_callable(callable $name[, bool $syntax_only = false])
4456 * Verify that the contents of a variable can be called as a function.
4457 * Parameters
4458 * $name
4459 * The callback function to check
4460 * $syntax_only
4461 * If set to TRUE the function only verifies that name might be a function or method.
4462 * It will only reject simple variables that are not strings, or an array that does
4463 * not have a valid structure to be used as a callback. The valid ones are supposed
4464 * to have only 2 entries, the first of which is an object or a string, and the second
4465 * a string.
4466 * Return
4467 * TRUE if name is callable, FALSE otherwise.
4468 */
4469static int vm_builtin_is_callable(jx9_context *pCtx, int nArg, jx9_value **apArg)
4470{
4471 jx9_vm *pVm;
4472 int res;
4473 if( nArg < 1 ){
4474 /* Missing arguments, return FALSE */
4475 jx9_result_bool(pCtx, 0);
4476 return SXRET_OK;
4477 }
4478 /* Point to the target VM */
4479 pVm = pCtx->pVm;
4480 /* Perform the requested operation */
4481 res = jx9VmIsCallable(pVm, apArg[0]);
4482 jx9_result_bool(pCtx, res);
4483 return SXRET_OK;
4484}
4485/*
4486 * Hash walker callback used by the [get_defined_functions()] function
4487 * defined below.
4488 */
4489static int VmHashFuncStep(SyHashEntry *pEntry, void *pUserData)
4490{
4491 jx9_value *pArray = (jx9_value *)pUserData;
4492 jx9_value sName;
4493 sxi32 rc;
4494 /* Prepare the function name for insertion */
4495 jx9MemObjInitFromString(pArray->pVm, &sName, 0);
4496 jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
4497 /* Perform the insertion */
4498 rc = jx9_array_add_elem(pArray, 0/* Automatic index assign */, &sName); /* Will make it's own copy */
4499 jx9MemObjRelease(&sName);
4500 return rc;
4501}
4502/*
4503 * array get_defined_functions(void)
4504 * Returns an array of all defined functions.
4505 * Parameter
4506 * None.
4507 * Return
4508 * Returns an multidimensional array containing a list of all defined functions
4509 * both built-in (internal) and user-defined.
4510 * The internal functions will be accessible via $arr["internal"], and the user
4511 * defined ones using $arr["user"].
4512 * Note:
4513 * NULL is returned on failure.
4514 */
4515static int vm_builtin_get_defined_func(jx9_context *pCtx, int nArg, jx9_value **apArg)
4516{
4517 jx9_value *pArray;
4518 /* NOTE:
4519 * Don't worry about freeing memory here, every allocated resource will be released
4520 * automatically by the engine as soon we return from this foreign function.
4521 */
4522 pArray = jx9_context_new_array(pCtx);
4523 if( pArray == 0 ){
4524 SXUNUSED(nArg); /* cc warning */
4525 SXUNUSED(apArg);
4526 /* Return NULL */
4527 jx9_result_null(pCtx);
4528 return SXRET_OK;
4529 }
4530 /* Fill with the appropriate information */
4531 SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pArray);
4532 /* Fill with the appropriate information */
4533 SyHashForEach(&pCtx->pVm->hFunction, VmHashFuncStep,pArray);
4534 /* Return a copy of the array array */
4535 jx9_result_value(pCtx, pArray);
4536 return SXRET_OK;
4537}
4538/*
4539 * Call a user defined or foreign function where the name of the function
4540 * is stored in the pFunc parameter and the given arguments are stored
4541 * in the apArg[] array.
4542 * Return SXRET_OK if the function was successfuly called.Any other
4543 * return value indicates failure.
4544 */
4545JX9_PRIVATE sxi32 jx9VmCallUserFunction(
4546 jx9_vm *pVm, /* Target VM */
4547 jx9_value *pFunc, /* Callback name */
4548 int nArg, /* Total number of given arguments */
4549 jx9_value **apArg, /* Callback arguments */
4550 jx9_value *pResult /* Store callback return value here. NULL otherwise */
4551 )
4552{
4553 jx9_value *aStack;
4554 VmInstr aInstr[2];
4555 int i;
4556 if((pFunc->iFlags & (MEMOBJ_STRING)) == 0 ){
4557 /* Don't bother processing, it's invalid anyway */
4558 if( pResult ){
4559 /* Assume a null return value */
4560 jx9MemObjRelease(pResult);
4561 }
4562 return SXERR_INVALID;
4563 }
4564 /* Create a new operand stack */
4565 aStack = VmNewOperandStack(&(*pVm), 1+nArg);
4566 if( aStack == 0 ){
4567 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
4568 "JX9 is running out of memory while invoking user callback");
4569 if( pResult ){
4570 /* Assume a null return value */
4571 jx9MemObjRelease(pResult);
4572 }
4573 return SXERR_MEM;
4574 }
4575 /* Fill the operand stack with the given arguments */
4576 for( i = 0 ; i < nArg ; i++ ){
4577 jx9MemObjLoad(apArg[i], &aStack[i]);
4578 aStack[i].nIdx = apArg[i]->nIdx;
4579 }
4580 /* Push the function name */
4581 jx9MemObjLoad(pFunc, &aStack[i]);
4582 aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */
4583 /* Emit the CALL istruction */
4584 aInstr[0].iOp = JX9_OP_CALL;
4585 aInstr[0].iP1 = nArg; /* Total number of given arguments */
4586 aInstr[0].iP2 = 0;
4587 aInstr[0].p3 = 0;
4588 /* Emit the DONE instruction */
4589 aInstr[1].iOp = JX9_OP_DONE;
4590 aInstr[1].iP1 = 1; /* Extract function return value if available */
4591 aInstr[1].iP2 = 0;
4592 aInstr[1].p3 = 0;
4593 /* Execute the function body (if available) */
4594 VmByteCodeExec(&(*pVm), aInstr, aStack, nArg, pResult);
4595 /* Clean up the mess left behind */
4596 SyMemBackendFree(&pVm->sAllocator, aStack);
4597 return JX9_OK;
4598}
4599/*
4600 * Call a user defined or foreign function whith a varibale number
4601 * of arguments where the name of the function is stored in the pFunc
4602 * parameter.
4603 * Return SXRET_OK if the function was successfuly called.Any other
4604 * return value indicates failure.
4605 */
4606JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(
4607 jx9_vm *pVm, /* Target VM */
4608 jx9_value *pFunc, /* Callback name */
4609 jx9_value *pResult, /* Store callback return value here. NULL otherwise */
4610 ... /* 0 (Zero) or more Callback arguments */
4611 )
4612{
4613 jx9_value *pArg;
4614 SySet aArg;
4615 va_list ap;
4616 sxi32 rc;
4617 SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
4618 /* Copy arguments one after one */
4619 va_start(ap, pResult);
4620 for(;;){
4621 pArg = va_arg(ap, jx9_value *);
4622 if( pArg == 0 ){
4623 break;
4624 }
4625 SySetPut(&aArg, (const void *)&pArg);
4626 }
4627 /* Call the core routine */
4628 rc = jx9VmCallUserFunction(&(*pVm), pFunc, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg), pResult);
4629 /* Cleanup */
4630 SySetRelease(&aArg);
4631 return rc;
4632}
4633/*
4634 * bool defined(string $name)
4635 * Checks whether a given named constant exists.
4636 * Parameter:
4637 * Name of the desired constant.
4638 * Return
4639 * TRUE if the given constant exists.FALSE otherwise.
4640 */
4641static int vm_builtin_defined(jx9_context *pCtx, int nArg, jx9_value **apArg)
4642{
4643 const char *zName;
4644 int nLen = 0;
4645 int res = 0;
4646 if( nArg < 1 ){
4647 /* Missing constant name, return FALSE */
4648 jx9_context_throw_error(pCtx,JX9_CTX_NOTICE,"Missing constant name");
4649 jx9_result_bool(pCtx, 0);
4650 return SXRET_OK;
4651 }
4652 /* Extract constant name */
4653 zName = jx9_value_to_string(apArg[0], &nLen);
4654 /* Perform the lookup */
4655 if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant, (const void *)zName, (sxu32)nLen) != 0 ){
4656 /* Already defined */
4657 res = 1;
4658 }
4659 jx9_result_bool(pCtx, res);
4660 return SXRET_OK;
4661}
4662/*
4663 * Hash walker callback used by the [get_defined_constants()] function
4664 * defined below.
4665 */
4666static int VmHashConstStep(SyHashEntry *pEntry, void *pUserData)
4667{
4668 jx9_value *pArray = (jx9_value *)pUserData;
4669 jx9_value sName;
4670 sxi32 rc;
4671 /* Prepare the constant name for insertion */
4672 jx9MemObjInitFromString(pArray->pVm, &sName, 0);
4673 jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
4674 /* Perform the insertion */
4675 rc = jx9_array_add_elem(pArray, 0, &sName); /* Will make it's own copy */
4676 jx9MemObjRelease(&sName);
4677 return rc;
4678}
4679/*
4680 * array get_defined_constants(void)
4681 * Returns an associative array with the names of all defined
4682 * constants.
4683 * Parameters
4684 * NONE.
4685 * Returns
4686 * Returns the names of all the constants currently defined.
4687 */
4688static int vm_builtin_get_defined_constants(jx9_context *pCtx, int nArg, jx9_value **apArg)
4689{
4690 jx9_value *pArray;
4691 /* Create the array first*/
4692 pArray = jx9_context_new_array(pCtx);
4693 if( pArray == 0 ){
4694 SXUNUSED(nArg); /* cc warning */
4695 SXUNUSED(apArg);
4696 /* Return NULL */
4697 jx9_result_null(pCtx);
4698 return SXRET_OK;
4699 }
4700 /* Fill the array with the defined constants */
4701 SyHashForEach(&pCtx->pVm->hConstant, VmHashConstStep, pArray);
4702 /* Return the created array */
4703 jx9_result_value(pCtx, pArray);
4704 return SXRET_OK;
4705}
4706/*
4707 * Section:
4708 * Random numbers/string generators.
4709 * Authors:
4710 * Symisc Systems, devel@symisc.net.
4711 * Copyright (C) Symisc Systems, http://jx9.symisc.net
4712 * Status:
4713 * Stable.
4714 */
4715/*
4716 * Generate a random 32-bit unsigned integer.
4717 * JX9 use it's own private PRNG which is based on the one
4718 * used by te SQLite3 library.
4719 */
4720JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm)
4721{
4722 sxu32 iNum;
4723 SyRandomness(&pVm->sPrng, (void *)&iNum, sizeof(sxu32));
4724 return iNum;
4725}
4726/*
4727 * Generate a random string (English Alphabet) of length nLen.
4728 * Note that the generated string is NOT null terminated.
4729 * JX9 use it's own private PRNG which is based on the one used
4730 * by te SQLite3 library.
4731 */
4732JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen)
4733{
4734 static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
4735 int i;
4736 /* Generate a binary string first */
4737 SyRandomness(&pVm->sPrng, zBuf, (sxu32)nLen);
4738 /* Turn the binary string into english based alphabet */
4739 for( i = 0 ; i < nLen ; ++i ){
4740 zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
4741 }
4742}
4743/*
4744 * int rand()
4745 * Generate a random (unsigned 32-bit) integer.
4746 * Parameter
4747 * $min
4748 * The lowest value to return (default: 0)
4749 * $max
4750 * The highest value to return (default: getrandmax())
4751 * Return
4752 * A pseudo random value between min (or 0) and max (or getrandmax(), inclusive).
4753 * Note:
4754 * JX9 use it's own private PRNG which is based on the one used
4755 * by te SQLite3 library.
4756 */
4757static int vm_builtin_rand(jx9_context *pCtx, int nArg, jx9_value **apArg)
4758{
4759 sxu32 iNum;
4760 /* Generate the random number */
4761 iNum = jx9VmRandomNum(pCtx->pVm);
4762 if( nArg > 1 ){
4763 sxu32 iMin, iMax;
4764 iMin = (sxu32)jx9_value_to_int(apArg[0]);
4765 iMax = (sxu32)jx9_value_to_int(apArg[1]);
4766 if( iMin < iMax ){
4767 sxu32 iDiv = iMax+1-iMin;
4768 if( iDiv > 0 ){
4769 iNum = (iNum % iDiv)+iMin;
4770 }
4771 }else if(iMax > 0 ){
4772 iNum %= iMax;
4773 }
4774 }
4775 /* Return the number */
4776 jx9_result_int64(pCtx, (jx9_int64)iNum);
4777 return SXRET_OK;
4778}
4779/*
4780 * int getrandmax(void)
4781 * Show largest possible random value
4782 * Return
4783 * The largest possible random value returned by rand() which is in
4784 * this implementation 0xFFFFFFFF.
4785 * Note:
4786 * JX9 use it's own private PRNG which is based on the one used
4787 * by te SQLite3 library.
4788 */
4789static int vm_builtin_getrandmax(jx9_context *pCtx, int nArg, jx9_value **apArg)
4790{
4791 SXUNUSED(nArg); /* cc warning */
4792 SXUNUSED(apArg);
4793 jx9_result_int64(pCtx, SXU32_HIGH);
4794 return SXRET_OK;
4795}
4796/*
4797 * string rand_str()
4798 * string rand_str(int $len)
4799 * Generate a random string (English alphabet).
4800 * Parameter
4801 * $len
4802 * Length of the desired string (default: 16, Min: 1, Max: 1024)
4803 * Return
4804 * A pseudo random string.
4805 * Note:
4806 * JX9 use it's own private PRNG which is based on the one used
4807 * by te SQLite3 library.
4808 */
4809static int vm_builtin_rand_str(jx9_context *pCtx, int nArg, jx9_value **apArg)
4810{
4811 char zString[1024];
4812 int iLen = 0x10;
4813 if( nArg > 0 ){
4814 /* Get the desired length */
4815 iLen = jx9_value_to_int(apArg[0]);
4816 if( iLen < 1 || iLen > 1024 ){
4817 /* Default length */
4818 iLen = 0x10;
4819 }
4820 }
4821 /* Generate the random string */
4822 jx9VmRandomString(pCtx->pVm, zString, iLen);
4823 /* Return the generated string */
4824 jx9_result_string(pCtx, zString, iLen); /* Will make it's own copy */
4825 return SXRET_OK;
4826}
4827/*
4828 * Section:
4829 * Language construct implementation as foreign functions.
4830 * Authors:
4831 * Symisc Systems, devel@symisc.net.
4832 * Copyright (C) Symisc Systems, http://jx9.symisc.net
4833 * Status:
4834 * Stable.
4835 */
4836/*
4837 * void print($string...)
4838 * Output one or more messages.
4839 * Parameters
4840 * $string
4841 * Message to output.
4842 * Return
4843 * NULL.
4844 */
4845static int vm_builtin_print(jx9_context *pCtx, int nArg,jx9_value **apArg)
4846{
4847 const char *zData;
4848 int nDataLen = 0;
4849 jx9_vm *pVm;
4850 int i, rc;
4851 /* Point to the target VM */
4852 pVm = pCtx->pVm;
4853 /* Output */
4854 for( i = 0 ; i < nArg ; ++i ){
4855 zData = jx9_value_to_string(apArg[i], &nDataLen);
4856 if( nDataLen > 0 ){
4857 rc = pVm->sVmConsumer.xConsumer((const void *)zData, (unsigned int)nDataLen, pVm->sVmConsumer.pUserData);
4858 /* Increment output length */
4859 pVm->nOutputLen += nDataLen;
4860 if( rc == SXERR_ABORT ){
4861 /* Output consumer callback request an operation abort */
4862 return JX9_ABORT;
4863 }
4864 }
4865 }
4866 return SXRET_OK;
4867}
4868/*
4869 * void exit(string $msg)
4870 * void exit(int $status)
4871 * void die(string $ms)
4872 * void die(int $status)
4873 * Output a message and terminate program execution.
4874 * Parameter
4875 * If status is a string, this function prints the status just before exiting.
4876 * If status is an integer, that value will be used as the exit status
4877 * and not printed
4878 * Return
4879 * NULL
4880 */
4881static int vm_builtin_exit(jx9_context *pCtx, int nArg, jx9_value **apArg)
4882{
4883 if( nArg > 0 ){
4884 if( jx9_value_is_string(apArg[0]) ){
4885 const char *zData;
4886 int iLen = 0;
4887 /* Print exit message */
4888 zData = jx9_value_to_string(apArg[0], &iLen);
4889 jx9_context_output(pCtx, zData, iLen);
4890 }else if(jx9_value_is_int(apArg[0]) ){
4891 sxi32 iExitStatus;
4892 /* Record exit status code */
4893 iExitStatus = jx9_value_to_int(apArg[0]);
4894 pCtx->pVm->iExitStatus = iExitStatus;
4895 }
4896 }
4897 /* Abort processing immediately */
4898 return JX9_ABORT;
4899}
4900/*
4901 * Unset a memory object [i.e: a jx9_value].
4902 */
4903JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm,sxu32 nObjIdx)
4904{
4905 jx9_value *pObj;
4906 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nObjIdx);
4907 if( pObj ){
4908 VmSlot sFree;
4909 /* Release the object */
4910 jx9MemObjRelease(pObj);
4911 /* Restore to the free list */
4912 sFree.nIdx = nObjIdx;
4913 sFree.pUserData = 0;
4914 SySetPut(&pVm->aFreeObj, (const void *)&sFree);
4915 }
4916 return SXRET_OK;
4917}
4918/*
4919 * string gettype($var)
4920 * Get the type of a variable
4921 * Parameters
4922 * $var
4923 * The variable being type checked.
4924 * Return
4925 * String representation of the given variable type.
4926 */
4927static int vm_builtin_gettype(jx9_context *pCtx, int nArg, jx9_value **apArg)
4928{
4929 const char *zType = "null";
4930 if( nArg > 0 ){
4931 zType = jx9MemObjTypeDump(apArg[0]);
4932 }
4933 /* Return the variable type */
4934 jx9_result_string(pCtx, zType, -1/*Compute length automatically*/);
4935 return SXRET_OK;
4936}
4937/*
4938 * string get_resource_type(resource $handle)
4939 * This function gets the type of the given resource.
4940 * Parameters
4941 * $handle
4942 * The evaluated resource handle.
4943 * Return
4944 * If the given handle is a resource, this function will return a string
4945 * representing its type. If the type is not identified by this function
4946 * the return value will be the string Unknown.
4947 * This function will return FALSE and generate an error if handle
4948 * is not a resource.
4949 */
4950static int vm_builtin_get_resource_type(jx9_context *pCtx, int nArg, jx9_value **apArg)
4951{
4952 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
4953 /* Missing/Invalid arguments, return FALSE*/
4954 jx9_result_bool(pCtx, 0);
4955 return SXRET_OK;
4956 }
4957 jx9_result_string_format(pCtx, "resID_%#x", apArg[0]->x.pOther);
4958 return SXRET_OK;
4959}
4960/*
4961 * void dump(expression, ....)
4962 * dump — Dumps information about a variable
4963 * Parameters
4964 * One or more expression to dump.
4965 * Returns
4966 * Nothing.
4967 */
4968static int vm_builtin_dump(jx9_context *pCtx, int nArg, jx9_value **apArg)
4969{
4970 SyBlob sDump; /* Generated dump is stored here */
4971 int i;
4972 SyBlobInit(&sDump,&pCtx->pVm->sAllocator);
4973 /* Dump one or more expressions */
4974 for( i = 0 ; i < nArg ; i++ ){
4975 jx9_value *pObj = apArg[i];
4976 /* Reset the working buffer */
4977 SyBlobReset(&sDump);
4978 /* Dump the given expression */
4979 jx9MemObjDump(&sDump,pObj);
4980 /* Output */
4981 if( SyBlobLength(&sDump) > 0 ){
4982 jx9_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
4983 }
4984 }
4985 /* Release the working buffer */
4986 SyBlobRelease(&sDump);
4987 return SXRET_OK;
4988}
4989/*
4990 * Section:
4991 * Version, Credits and Copyright related functions.
4992 * Authors:
4993 * Symisc Systems, devel@symisc.net.
4994 * Copyright (C) Symisc Systems, http://jx9.symisc.net
4995 * Stable.
4996 */
4997/*
4998 * string jx9_version(void)
4999 * string jx9_credits(void)
5000 * Returns the running version of the jx9 version.
5001 * Parameters
5002 * None
5003 * Return
5004 * Current jx9 version.
5005 */
5006static int vm_builtin_jx9_version(jx9_context *pCtx, int nArg, jx9_value **apArg)
5007{
5008 SXUNUSED(nArg);
5009 SXUNUSED(apArg); /* cc warning */
5010 /* Current engine version, signature and cipyright notice */
5011 jx9_result_string_format(pCtx,"%s %s, %s",JX9_VERSION,JX9_SIG,JX9_COPYRIGHT);
5012 return JX9_OK;
5013}
5014/*
5015 * Section:
5016 * URL related routines.
5017 * Authors:
5018 * Symisc Systems, devel@symisc.net.
5019 * Copyright (C) Symisc Systems, http://jx9.symisc.net
5020 * Status:
5021 * Stable.
5022 */
5023/* Forward declaration */
5024static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen);
5025/*
5026 * value parse_url(string $url [, int $component = -1 ])
5027 * Parse a URL and return its fields.
5028 * Parameters
5029 * $url
5030 * The URL to parse.
5031 * $component
5032 * Specify one of JX9_URL_SCHEME, JX9_URL_HOST, JX9_URL_PORT, JX9_URL_USER
5033 * JX9_URL_PASS, JX9_URL_PATH, JX9_URL_QUERY or JX9_URL_FRAGMENT to retrieve
5034 * just a specific URL component as a string (except when JX9_URL_PORT is given
5035 * in which case the return value will be an integer).
5036 * Return
5037 * If the component parameter is omitted, an associative array is returned.
5038 * At least one element will be present within the array. Potential keys within
5039 * this array are:
5040 * scheme - e.g. http
5041 * host
5042 * port
5043 * user
5044 * pass
5045 * path
5046 * query - after the question mark ?
5047 * fragment - after the hashmark #
5048 * Note:
5049 * FALSE is returned on failure.
5050 * This function work with relative URL unlike the one shipped
5051 * with the standard JX9 engine.
5052 */
5053static int vm_builtin_parse_url(jx9_context *pCtx, int nArg, jx9_value **apArg)
5054{
5055 const char *zStr; /* Input string */
5056 SyString *pComp; /* Pointer to the URI component */
5057 SyhttpUri sURI; /* Parse of the given URI */
5058 int nLen;
5059 sxi32 rc;
5060 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
5061 /* Missing/Invalid arguments, return FALSE */
5062 jx9_result_bool(pCtx, 0);
5063 return JX9_OK;
5064 }
5065 /* Extract the given URI */
5066 zStr = jx9_value_to_string(apArg[0], &nLen);
5067 if( nLen < 1 ){
5068 /* Nothing to process, return FALSE */
5069 jx9_result_bool(pCtx, 0);
5070 return JX9_OK;
5071 }
5072 /* Get a parse */
5073 rc = VmHttpSplitURI(&sURI, zStr, (sxu32)nLen);
5074 if( rc != SXRET_OK ){
5075 /* Malformed input, return FALSE */
5076 jx9_result_bool(pCtx, 0);
5077 return JX9_OK;
5078 }
5079 if( nArg > 1 ){
5080 int nComponent = jx9_value_to_int(apArg[1]);
5081 /* Refer to constant.c for constants values */
5082 switch(nComponent){
5083 case 1: /* JX9_URL_SCHEME */
5084 pComp = &sURI.sScheme;
5085 if( pComp->nByte < 1 ){
5086 /* No available value, return NULL */
5087 jx9_result_null(pCtx);
5088 }else{
5089 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
5090 }
5091 break;
5092 case 2: /* JX9_URL_HOST */
5093 pComp = &sURI.sHost;
5094 if( pComp->nByte < 1 ){
5095 /* No available value, return NULL */
5096 jx9_result_null(pCtx);
5097 }else{
5098 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
5099 }
5100 break;
5101 case 3: /* JX9_URL_PORT */
5102 pComp = &sURI.sPort;
5103 if( pComp->nByte < 1 ){
5104 /* No available value, return NULL */
5105 jx9_result_null(pCtx);
5106 }else{
5107 int iPort = 0;
5108 /* Cast the value to integer */
5109 SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
5110 jx9_result_int(pCtx, iPort);
5111 }
5112 break;
5113 case 4: /* JX9_URL_USER */
5114 pComp = &sURI.sUser;
5115 if( pComp->nByte < 1 ){
5116 /* No available value, return NULL */
5117 jx9_result_null(pCtx);
5118 }else{
5119 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
5120 }
5121 break;
5122 case 5: /* JX9_URL_PASS */
5123 pComp = &sURI.sPass;
5124 if( pComp->nByte < 1 ){
5125 /* No available value, return NULL */
5126 jx9_result_null(pCtx);
5127 }else{
5128 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
5129 }
5130 break;
5131 case 7: /* JX9_URL_QUERY */
5132 pComp = &sURI.sQuery;
5133 if( pComp->nByte < 1 ){
5134 /* No available value, return NULL */
5135 jx9_result_null(pCtx);
5136 }else{
5137 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
5138 }
5139 break;
5140 case 8: /* JX9_URL_FRAGMENT */
5141 pComp = &sURI.sFragment;
5142 if( pComp->nByte < 1 ){
5143 /* No available value, return NULL */
5144 jx9_result_null(pCtx);
5145 }else{
5146 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
5147 }
5148 break;
5149 case 6: /* JX9_URL_PATH */
5150 pComp = &sURI.sPath;
5151 if( pComp->nByte < 1 ){
5152 /* No available value, return NULL */
5153 jx9_result_null(pCtx);
5154 }else{
5155 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
5156 }
5157 break;
5158 default:
5159 /* No such entry, return NULL */
5160 jx9_result_null(pCtx);
5161 break;
5162 }
5163 }else{
5164 jx9_value *pArray, *pValue;
5165 /* Return an associative array */
5166 pArray = jx9_context_new_array(pCtx); /* Empty array */
5167 pValue = jx9_context_new_scalar(pCtx); /* Array value */
5168 if( pArray == 0 || pValue == 0 ){
5169 /* Out of memory */
5170 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "jx9 engine is running out of memory");
5171 /* Return false */
5172 jx9_result_bool(pCtx, 0);
5173 return JX9_OK;
5174 }
5175 /* Fill the array */
5176 pComp = &sURI.sScheme;
5177 if( pComp->nByte > 0 ){
5178 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
5179 jx9_array_add_strkey_elem(pArray, "scheme", pValue); /* Will make it's own copy */
5180 }
5181 /* Reset the string cursor */
5182 jx9_value_reset_string_cursor(pValue);
5183 pComp = &sURI.sHost;
5184 if( pComp->nByte > 0 ){
5185 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
5186 jx9_array_add_strkey_elem(pArray, "host", pValue); /* Will make it's own copy */
5187 }
5188 /* Reset the string cursor */
5189 jx9_value_reset_string_cursor(pValue);
5190 pComp = &sURI.sPort;
5191 if( pComp->nByte > 0 ){
5192 int iPort = 0;/* cc warning */
5193 /* Convert to integer */
5194 SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
5195 jx9_value_int(pValue, iPort);
5196 jx9_array_add_strkey_elem(pArray, "port", pValue); /* Will make it's own copy */
5197 }
5198 /* Reset the string cursor */
5199 jx9_value_reset_string_cursor(pValue);
5200 pComp = &sURI.sUser;
5201 if( pComp->nByte > 0 ){
5202 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
5203 jx9_array_add_strkey_elem(pArray, "user", pValue); /* Will make it's own copy */
5204 }
5205 /* Reset the string cursor */
5206 jx9_value_reset_string_cursor(pValue);
5207 pComp = &sURI.sPass;
5208 if( pComp->nByte > 0 ){
5209 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
5210 jx9_array_add_strkey_elem(pArray, "pass", pValue); /* Will make it's own copy */
5211 }
5212 /* Reset the string cursor */
5213 jx9_value_reset_string_cursor(pValue);
5214 pComp = &sURI.sPath;
5215 if( pComp->nByte > 0 ){
5216 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
5217 jx9_array_add_strkey_elem(pArray, "path", pValue); /* Will make it's own copy */
5218 }
5219 /* Reset the string cursor */
5220 jx9_value_reset_string_cursor(pValue);
5221 pComp = &sURI.sQuery;
5222 if( pComp->nByte > 0 ){
5223 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
5224 jx9_array_add_strkey_elem(pArray, "query", pValue); /* Will make it's own copy */
5225 }
5226 /* Reset the string cursor */
5227 jx9_value_reset_string_cursor(pValue);
5228 pComp = &sURI.sFragment;
5229 if( pComp->nByte > 0 ){
5230 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
5231 jx9_array_add_strkey_elem(pArray, "fragment", pValue); /* Will make it's own copy */
5232 }
5233 /* Return the created array */
5234 jx9_result_value(pCtx, pArray);
5235 /* NOTE:
5236 * Don't worry about freeing 'pValue', everything will be released
5237 * automatically as soon we return from this function.
5238 */
5239 }
5240 /* All done */
5241 return JX9_OK;
5242}
5243/*
5244 * Section:
5245 * Array related routines.
5246 * Authors:
5247 * Symisc Systems, devel@symisc.net.
5248 * Copyright (C) Symisc Systems, http://jx9.symisc.net
5249 * Status:
5250 * Stable.
5251 * Note 2012-5-21 01:04:15:
5252 * Array related functions that need access to the underlying
5253 * virtual machine are implemented here rather than 'hashmap.c'
5254 */
5255/*
5256 * The [extract()] function store it's state information in an instance
5257 * of the following structure.
5258 */
5259typedef struct extract_aux_data extract_aux_data;
5260struct extract_aux_data
5261{
5262 jx9_vm *pVm; /* VM that own this instance */
5263 int iCount; /* Number of variables successfully imported */
5264 const char *zPrefix; /* Prefix name */
5265 int Prefixlen; /* Prefix length */
5266 int iFlags; /* Control flags */
5267 char zWorker[1024]; /* Working buffer */
5268};
5269/* Forward declaration */
5270static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData);
5271/*
5272 * int extract(array $var_array[, int $extract_type = EXTR_OVERWRITE[, string $prefix = NULL ]])
5273 * Import variables into the current symbol table from an array.
5274 * Parameters
5275 * $var_array
5276 * An associative array. This function treats keys as variable names and values
5277 * as variable values. For each key/value pair it will create a variable in the current symbol
5278 * table, subject to extract_type and prefix parameters.
5279 * You must use an associative array; a numerically indexed array will not produce results
5280 * unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID.
5281 * $extract_type
5282 * The way invalid/numeric keys and collisions are treated is determined by the extract_type.
5283 * It can be one of the following values:
5284 * EXTR_OVERWRITE
5285 * If there is a collision, overwrite the existing variable.
5286 * EXTR_SKIP
5287 * If there is a collision, don't overwrite the existing variable.
5288 * EXTR_PREFIX_SAME
5289 * If there is a collision, prefix the variable name with prefix.
5290 * EXTR_PREFIX_ALL
5291 * Prefix all variable names with prefix.
5292 * EXTR_PREFIX_INVALID
5293 * Only prefix invalid/numeric variable names with prefix.
5294 * EXTR_IF_EXISTS
5295 * Only overwrite the variable if it already exists in the current symbol table
5296 * otherwise do nothing.
5297 * This is useful for defining a list of valid variables and then extracting only those
5298 * variables you have defined out of $_REQUEST, for example.
5299 * EXTR_PREFIX_IF_EXISTS
5300 * Only create prefixed variable names if the non-prefixed version of the same variable exists in
5301 * the current symbol table.
5302 * $prefix
5303 * Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL
5304 * EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name
5305 * it is not imported into the symbol table. Prefixes are automatically separated from the array key by an
5306 * underscore character.
5307 * Return
5308 * Returns the number of variables successfully imported into the symbol table.
5309 */
5310static int vm_builtin_extract(jx9_context *pCtx, int nArg, jx9_value **apArg)
5311{
5312 extract_aux_data sAux;
5313 jx9_hashmap *pMap;
5314 if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
5315 /* Missing/Invalid arguments, return 0 */
5316 jx9_result_int(pCtx, 0);
5317 return JX9_OK;
5318 }
5319 /* Point to the target hashmap */
5320 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
5321 if( pMap->nEntry < 1 ){
5322 /* Empty map, return 0 */
5323 jx9_result_int(pCtx, 0);
5324 return JX9_OK;
5325 }
5326 /* Prepare the aux data */
5327 SyZero(&sAux, sizeof(extract_aux_data)-sizeof(sAux.zWorker));
5328 if( nArg > 1 ){
5329 sAux.iFlags = jx9_value_to_int(apArg[1]);
5330 if( nArg > 2 ){
5331 sAux.zPrefix = jx9_value_to_string(apArg[2], &sAux.Prefixlen);
5332 }
5333 }
5334 sAux.pVm = pCtx->pVm;
5335 /* Invoke the worker callback */
5336 jx9HashmapWalk(pMap, VmExtractCallback, &sAux);
5337 /* Number of variables successfully imported */
5338 jx9_result_int(pCtx, sAux.iCount);
5339 return JX9_OK;
5340}
5341/*
5342 * Worker callback for the [extract()] function defined
5343 * below.
5344 */
5345static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
5346{
5347 extract_aux_data *pAux = (extract_aux_data *)pUserData;
5348 int iFlags = pAux->iFlags;
5349 jx9_vm *pVm = pAux->pVm;
5350 jx9_value *pObj;
5351 SyString sVar;
5352 if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){
5353 iFlags |= 0x08; /*EXTR_PREFIX_ALL*/
5354 }
5355 /* Perform a string cast */
5356 jx9MemObjToString(pKey);
5357 if( SyBlobLength(&pKey->sBlob) < 1 ){
5358 /* Unavailable variable name */
5359 return SXRET_OK;
5360 }
5361 sVar.nByte = 0; /* cc warning */
5362 if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){
5363 sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker, sizeof(pAux->zWorker), "%.*s_%.*s",
5364 pAux->Prefixlen, pAux->zPrefix,
5365 SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
5366 );
5367 }else{
5368 sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob), pAux->zWorker,
5369 SXMIN(SyBlobLength(&pKey->sBlob), sizeof(pAux->zWorker)));
5370 }
5371 sVar.zString = pAux->zWorker;
5372 /* Try to extract the variable */
5373 pObj = VmExtractMemObj(pVm, &sVar, TRUE, FALSE);
5374 if( pObj ){
5375 /* Collision */
5376 if( iFlags & 0x02 /* EXTR_SKIP */ ){
5377 return SXRET_OK;
5378 }
5379 if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){
5380 if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){
5381 /* Already prefixed */
5382 return SXRET_OK;
5383 }
5384 sVar.nByte = SyBufferFormat(
5385 pAux->zWorker, sizeof(pAux->zWorker),
5386 "%.*s_%.*s",
5387 pAux->Prefixlen, pAux->zPrefix,
5388 SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
5389 );
5390 pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
5391 }
5392 }else{
5393 /* Create the variable */
5394 pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
5395 }
5396 if( pObj ){
5397 /* Overwrite the old value */
5398 jx9MemObjStore(pValue, pObj);
5399 /* Increment counter */
5400 pAux->iCount++;
5401 }
5402 return SXRET_OK;
5403}
5404/*
5405 * Compile and evaluate a JX9 chunk at run-time.
5406 * Refer to the include language construct implementation for more
5407 * information.
5408 */
5409static sxi32 VmEvalChunk(
5410 jx9_vm *pVm, /* Underlying Virtual Machine */
5411 jx9_context *pCtx, /* Call Context */
5412 SyString *pChunk, /* JX9 chunk to evaluate */
5413 int iFlags, /* Compile flag */
5414 int bTrueReturn /* TRUE to return execution result */
5415 )
5416{
5417 SySet *pByteCode, aByteCode;
5418 ProcConsumer xErr = 0;
5419 void *pErrData = 0;
5420 /* Initialize bytecode container */
5421 SySetInit(&aByteCode, &pVm->sAllocator, sizeof(VmInstr));
5422 SySetAlloc(&aByteCode, 0x20);
5423 /* Reset the code generator */
5424 if( bTrueReturn ){
5425 /* Included file, log compile-time errors */
5426 xErr = pVm->pEngine->xConf.xErr;
5427 pErrData = pVm->pEngine->xConf.pErrData;
5428 }
5429 jx9ResetCodeGenerator(pVm, xErr, pErrData);
5430 /* Swap bytecode container */
5431 pByteCode = pVm->pByteContainer;
5432 pVm->pByteContainer = &aByteCode;
5433 /* Compile the chunk */
5434 jx9CompileScript(pVm, pChunk, iFlags);
5435 if( pVm->sCodeGen.nErr > 0 ){
5436 /* Compilation error, return false */
5437 if( pCtx ){
5438 jx9_result_bool(pCtx, 0);
5439 }
5440 }else{
5441 jx9_value sResult; /* Return value */
5442 if( SXRET_OK != jx9VmEmitInstr(pVm, JX9_OP_DONE, 0, 0, 0, 0) ){
5443 /* Out of memory */
5444 if( pCtx ){
5445 jx9_result_bool(pCtx, 0);
5446 }
5447 goto Cleanup;
5448 }
5449 if( bTrueReturn ){
5450 /* Assume a boolean true return value */
5451 jx9MemObjInitFromBool(pVm, &sResult, 1);
5452 }else{
5453 /* Assume a null return value */
5454 jx9MemObjInit(pVm, &sResult);
5455 }
5456 /* Execute the compiled chunk */
5457 VmLocalExec(pVm, &aByteCode, &sResult);
5458 if( pCtx ){
5459 /* Set the execution result */
5460 jx9_result_value(pCtx, &sResult);
5461 }
5462 jx9MemObjRelease(&sResult);
5463 }
5464Cleanup:
5465 /* Cleanup the mess left behind */
5466 pVm->pByteContainer = pByteCode;
5467 SySetRelease(&aByteCode);
5468 return SXRET_OK;
5469}
5470/*
5471 * Check if a file path is already included.
5472 */
5473static int VmIsIncludedFile(jx9_vm *pVm, SyString *pFile)
5474{
5475 SyString *aEntries;
5476 sxu32 n;
5477 aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded);
5478 /* Perform a linear search */
5479 for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){
5480 if( SyStringCmp(pFile, &aEntries[n], SyMemcmp) == 0 ){
5481 /* Already included */
5482 return TRUE;
5483 }
5484 }
5485 return FALSE;
5486}
5487/*
5488 * Push a file path in the appropriate VM container.
5489 */
5490JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew)
5491{
5492 SyString sPath;
5493 char *zDup;
5494#ifdef __WINNT__
5495 char *zCur;
5496#endif
5497 sxi32 rc;
5498 if( nLen < 0 ){
5499 nLen = SyStrlen(zPath);
5500 }
5501 /* Duplicate the file path first */
5502 zDup = SyMemBackendStrDup(&pVm->sAllocator, zPath, nLen);
5503 if( zDup == 0 ){
5504 return SXERR_MEM;
5505 }
5506#ifdef __WINNT__
5507 /* Normalize path on windows
5508 * Example:
5509 * Path/To/File.jx9
5510 * becomes
5511 * path\to\file.jx9
5512 */
5513 zCur = zDup;
5514 while( zCur[0] != 0 ){
5515 if( zCur[0] == '/' ){
5516 zCur[0] = '\\';
5517 }else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){
5518 int c = SyToLower(zCur[0]);
5519 zCur[0] = (char)c; /* MSVC stupidity */
5520 }
5521 zCur++;
5522 }
5523#endif
5524 /* Install the file path */
5525 SyStringInitFromBuf(&sPath, zDup, nLen);
5526 if( !bMain ){
5527 if( VmIsIncludedFile(&(*pVm), &sPath) ){
5528 /* Already included */
5529 *pNew = 0;
5530 }else{
5531 /* Insert in the corresponding container */
5532 rc = SySetPut(&pVm->aIncluded, (const void *)&sPath);
5533 if( rc != SXRET_OK ){
5534 SyMemBackendFree(&pVm->sAllocator, zDup);
5535 return rc;
5536 }
5537 *pNew = 1;
5538 }
5539 }
5540 SySetPut(&pVm->aFiles, (const void *)&sPath);
5541 return SXRET_OK;
5542}
5543/*
5544 * Compile and Execute a JX9 script at run-time.
5545 * SXRET_OK is returned on sucessful evaluation.Any other return values
5546 * indicates failure.
5547 * Note that the JX9 script to evaluate can be a local or remote file.In
5548 * either cases the [jx9StreamReadWholeFile()] function handle all the underlying
5549 * operations.
5550 * If the [jJX9_DISABLE_BUILTIN_FUNC] compile-time directive is defined, then
5551 * this function is a no-op.
5552 * Refer to the implementation of the include(), import() language
5553 * constructs for more information.
5554 */
5555static sxi32 VmExecIncludedFile(
5556 jx9_context *pCtx, /* Call Context */
5557 SyString *pPath, /* Script path or URL*/
5558 int IncludeOnce /* TRUE if called from import() or require_once() */
5559 )
5560{
5561 sxi32 rc;
5562#ifndef JX9_DISABLE_BUILTIN_FUNC
5563 const jx9_io_stream *pStream;
5564 SyBlob sContents;
5565 void *pHandle;
5566 jx9_vm *pVm;
5567 int isNew;
5568 /* Initialize fields */
5569 pVm = pCtx->pVm;
5570 SyBlobInit(&sContents, &pVm->sAllocator);
5571 isNew = 0;
5572 /* Extract the associated stream */
5573 pStream = jx9VmGetStreamDevice(pVm, &pPath->zString, pPath->nByte);
5574 /*
5575 * Open the file or the URL [i.e: http://jx9.symisc.net/example/hello.jx9.txt"]
5576 * in a read-only mode.
5577 */
5578 pHandle = jx9StreamOpenHandle(pVm, pStream,pPath->zString, JX9_IO_OPEN_RDONLY, TRUE, 0, TRUE, &isNew);
5579 if( pHandle == 0 ){
5580 return SXERR_IO;
5581 }
5582 rc = SXRET_OK; /* Stupid cc warning */
5583 if( IncludeOnce && !isNew ){
5584 /* Already included */
5585 rc = SXERR_EXISTS;
5586 }else{
5587 /* Read the whole file contents */
5588 rc = jx9StreamReadWholeFile(pHandle, pStream, &sContents);
5589 if( rc == SXRET_OK ){
5590 SyString sScript;
5591 /* Compile and execute the script */
5592 SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents));
5593 VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, 0, TRUE);
5594 }
5595 }
5596 /* Pop from the set of included file */
5597 (void)SySetPop(&pVm->aFiles);
5598 /* Close the handle */
5599 jx9StreamCloseHandle(pStream, pHandle);
5600 /* Release the working buffer */
5601 SyBlobRelease(&sContents);
5602#else
5603 pCtx = 0; /* cc warning */
5604 pPath = 0;
5605 IncludeOnce = 0;
5606 rc = SXERR_IO;
5607#endif /* JX9_DISABLE_BUILTIN_FUNC */
5608 return rc;
5609}
5610/* * include:
5611 * According to the JX9 reference manual.
5612 * The include() function includes and evaluates the specified file.
5613 * Files are included based on the file path given or, if none is given
5614 * the include_path specified.If the file isn't found in the include_path
5615 * include() will finally check in the calling script's own directory
5616 * and the current working directory before failing. The include()
5617 * construct will emit a warning if it cannot find a file; this is different
5618 * behavior from require(), which will emit a fatal error.
5619 * If a path is defined — whether absolute (starting with a drive letter
5620 * or \ on Windows, or / on Unix/Linux systems) or relative to the current
5621 * directory (starting with . or ..) — the include_path will be ignored altogether.
5622 * For example, if a filename begins with ../, the parser will look in the parent
5623 * directory to find the requested file.
5624 * When a file is included, the code it contains inherits the variable scope
5625 * of the line on which the include occurs. Any variables available at that line
5626 * in the calling file will be available within the called file, from that point forward.
5627 * However, all functions and objectes defined in the included file have the global scope.
5628 */
5629static int vm_builtin_include(jx9_context *pCtx, int nArg, jx9_value **apArg)
5630{
5631 SyString sFile;
5632 sxi32 rc;
5633 if( nArg < 1 ){
5634 /* Nothing to evaluate, return NULL */
5635 jx9_result_null(pCtx);
5636 return SXRET_OK;
5637 }
5638 /* File to include */
5639 sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
5640 if( sFile.nByte < 1 ){
5641 /* Empty string, return NULL */
5642 jx9_result_null(pCtx);
5643 return SXRET_OK;
5644 }
5645 /* Open, compile and execute the desired script */
5646 rc = VmExecIncludedFile(&(*pCtx), &sFile, FALSE);
5647 if( rc != SXRET_OK ){
5648 /* Emit a warning and return false */
5649 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
5650 jx9_result_bool(pCtx, 0);
5651 }
5652 return SXRET_OK;
5653}
5654/*
5655 * import:
5656 * According to the JX9 reference manual.
5657 * The import() statement includes and evaluates the specified file during
5658 * the execution of the script. This is a behavior similar to the include()
5659 * statement, with the only difference being that if the code from a file has already
5660 * been included, it will not be included again. As the name suggests, it will be included
5661 * just once.
5662 */
5663static int vm_builtin_import(jx9_context *pCtx, int nArg, jx9_value **apArg)
5664{
5665 SyString sFile;
5666 sxi32 rc;
5667 if( nArg < 1 ){
5668 /* Nothing to evaluate, return NULL */
5669 jx9_result_null(pCtx);
5670 return SXRET_OK;
5671 }
5672 /* File to include */
5673 sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
5674 if( sFile.nByte < 1 ){
5675 /* Empty string, return NULL */
5676 jx9_result_null(pCtx);
5677 return SXRET_OK;
5678 }
5679 /* Open, compile and execute the desired script */
5680 rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE);
5681 if( rc == SXERR_EXISTS ){
5682 /* File already included, return TRUE */
5683 jx9_result_bool(pCtx, 1);
5684 return SXRET_OK;
5685 }
5686 if( rc != SXRET_OK ){
5687 /* Emit a warning and return false */
5688 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
5689 jx9_result_bool(pCtx, 0);
5690 }
5691 return SXRET_OK;
5692}
5693/*
5694 * Section:
5695 * Command line arguments processing.
5696 * Authors:
5697 * Symisc Systems, devel@symisc.net.
5698 * Copyright (C) Symisc Systems, http://jx9.symisc.net
5699 * Status:
5700 * Stable.
5701 */
5702/*
5703 * Check if a short option argument [i.e: -c] is available in the command
5704 * line string. Return a pointer to the start of the stream on success.
5705 * NULL otherwise.
5706 */
5707static const char * VmFindShortOpt(int c, const char *zIn, const char *zEnd)
5708{
5709 while( zIn < zEnd ){
5710 if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){
5711 /* Got one */
5712 return &zIn[1];
5713 }
5714 /* Advance the cursor */
5715 zIn++;
5716 }
5717 /* No such option */
5718 return 0;
5719}
5720/*
5721 * Check if a long option argument [i.e: --opt] is available in the command
5722 * line string. Return a pointer to the start of the stream on success.
5723 * NULL otherwise.
5724 */
5725static const char * VmFindLongOpt(const char *zLong, int nByte, const char *zIn, const char *zEnd)
5726{
5727 const char *zOpt;
5728 while( zIn < zEnd ){
5729 if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){
5730 zIn += 2;
5731 zOpt = zIn;
5732 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
5733 if( zIn[0] == '=' /* --opt=val */){
5734 break;
5735 }
5736 zIn++;
5737 }
5738 /* Test */
5739 if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt, zLong, nByte) == 0 ){
5740 /* Got one, return it's value */
5741 return zIn;
5742 }
5743
5744 }else{
5745 zIn++;
5746 }
5747 }
5748 /* No such option */
5749 return 0;
5750}
5751/*
5752 * Long option [i.e: --opt] arguments private data structure.
5753 */
5754struct getopt_long_opt
5755{
5756 const char *zArgIn, *zArgEnd; /* Command line arguments */
5757 jx9_value *pWorker; /* Worker variable*/
5758 jx9_value *pArray; /* getopt() return value */
5759 jx9_context *pCtx; /* Call Context */
5760};
5761/* Forward declaration */
5762static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData);
5763/*
5764 * Extract short or long argument option values.
5765 */
5766static void VmExtractOptArgValue(
5767 jx9_value *pArray, /* getopt() return value */
5768 jx9_value *pWorker, /* Worker variable */
5769 const char *zArg, /* Argument stream */
5770 const char *zArgEnd, /* End of the argument stream */
5771 int need_val, /* TRUE to fetch option argument */
5772 jx9_context *pCtx, /* Call Context */
5773 const char *zName /* Option name */)
5774{
5775 jx9_value_bool(pWorker, 0);
5776 if( !need_val ){
5777 /*
5778 * Option does not need arguments.
5779 * Insert the option name and a boolean FALSE.
5780 */
5781 jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
5782 }else{
5783 const char *zCur;
5784 /* Extract option argument */
5785 zArg++;
5786 if( zArg < zArgEnd && zArg[0] == '=' ){
5787 zArg++;
5788 }
5789 while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
5790 zArg++;
5791 }
5792 if( zArg >= zArgEnd || zArg[0] == '-' ){
5793 /*
5794 * Argument not found.
5795 * Insert the option name and a boolean FALSE.
5796 */
5797 jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
5798 return;
5799 }
5800 /* Delimit the value */
5801 zCur = zArg;
5802 if( zArg[0] == '\'' || zArg[0] == '"' ){
5803 int d = zArg[0];
5804 /* Delimt the argument */
5805 zArg++;
5806 zCur = zArg;
5807 while( zArg < zArgEnd ){
5808 if( zArg[0] == d && zArg[-1] != '\\' ){
5809 /* Delimiter found, exit the loop */
5810 break;
5811 }
5812 zArg++;
5813 }
5814 /* Save the value */
5815 jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
5816 if( zArg < zArgEnd ){ zArg++; }
5817 }else{
5818 while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
5819 zArg++;
5820 }
5821 /* Save the value */
5822 jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
5823 }
5824 /*
5825 * Check if we are dealing with multiple values.
5826 * If so, create an array to hold them, rather than a scalar variable.
5827 */
5828 while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
5829 zArg++;
5830 }
5831 if( zArg < zArgEnd && zArg[0] != '-' ){
5832 jx9_value *pOptArg; /* Array of option arguments */
5833 pOptArg = jx9_context_new_array(pCtx);
5834 if( pOptArg == 0 ){
5835 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
5836 }else{
5837 /* Insert the first value */
5838 jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
5839 for(;;){
5840 if( zArg >= zArgEnd || zArg[0] == '-' ){
5841 /* No more value */
5842 break;
5843 }
5844 /* Delimit the value */
5845 zCur = zArg;
5846 if( zArg < zArgEnd && zArg[0] == '\\' ){
5847 zArg++;
5848 zCur = zArg;
5849 }
5850 while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
5851 zArg++;
5852 }
5853 /* Reset the string cursor */
5854 jx9_value_reset_string_cursor(pWorker);
5855 /* Save the value */
5856 jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
5857 /* Insert */
5858 jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
5859 /* Jump trailing white spaces */
5860 while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
5861 zArg++;
5862 }
5863 }
5864 /* Insert the option arg array */
5865 jx9_array_add_strkey_elem(pArray, (const char *)zName, pOptArg); /* Will make it's own copy */
5866 /* Safely release */
5867 jx9_context_release_value(pCtx, pOptArg);
5868 }
5869 }else{
5870 /* Single value */
5871 jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
5872 }
5873 }
5874}
5875/*
5876 * array getopt(string $options[, array $longopts ])
5877 * Gets options from the command line argument list.
5878 * Parameters
5879 * $options
5880 * Each character in this string will be used as option characters
5881 * and matched against options passed to the script starting with
5882 * a single hyphen (-). For example, an option string "x" recognizes
5883 * an option -x. Only a-z, A-Z and 0-9 are allowed.
5884 * $longopts
5885 * An array of options. Each element in this array will be used as option
5886 * strings and matched against options passed to the script starting with
5887 * two hyphens (--). For example, an longopts element "opt" recognizes an
5888 * option --opt.
5889 * Return
5890 * This function will return an array of option / argument pairs or FALSE
5891 * on failure.
5892 */
5893static int vm_builtin_getopt(jx9_context *pCtx, int nArg, jx9_value **apArg)
5894{
5895 const char *zIn, *zEnd, *zArg, *zArgIn, *zArgEnd;
5896 struct getopt_long_opt sLong;
5897 jx9_value *pArray, *pWorker;
5898 SyBlob *pArg;
5899 int nByte;
5900 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
5901 /* Missing/Invalid arguments, return FALSE */
5902 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Missing/Invalid option arguments");
5903 jx9_result_bool(pCtx, 0);
5904 return JX9_OK;
5905 }
5906 /* Extract option arguments */
5907 zIn = jx9_value_to_string(apArg[0], &nByte);
5908 zEnd = &zIn[nByte];
5909 /* Point to the string representation of the $argv[] array */
5910 pArg = &pCtx->pVm->sArgv;
5911 /* Create a new empty array and a worker variable */
5912 pArray = jx9_context_new_array(pCtx);
5913 pWorker = jx9_context_new_scalar(pCtx);
5914 if( pArray == 0 || pWorker == 0 ){
5915 jx9_context_throw_error(pCtx,JX9_CTX_ERR, "JX9 is running out of memory");
5916 jx9_result_bool(pCtx, 0);
5917 return JX9_OK;
5918 }
5919 if( SyBlobLength(pArg) < 1 ){
5920 /* Empty command line, return the empty array*/
5921 jx9_result_value(pCtx, pArray);
5922 /* Everything will be released automatically when we return
5923 * from this function.
5924 */
5925 return JX9_OK;
5926 }
5927 zArgIn = (const char *)SyBlobData(pArg);
5928 zArgEnd = &zArgIn[SyBlobLength(pArg)];
5929 /* Fill the long option structure */
5930 sLong.pArray = pArray;
5931 sLong.pWorker = pWorker;
5932 sLong.zArgIn = zArgIn;
5933 sLong.zArgEnd = zArgEnd;
5934 sLong.pCtx = pCtx;
5935 /* Start processing */
5936 while( zIn < zEnd ){
5937 int c = zIn[0];
5938 int need_val = 0;
5939 /* Advance the stream cursor */
5940 zIn++;
5941 /* Ignore non-alphanum characters */
5942 if( !SyisAlphaNum(c) ){
5943 continue;
5944 }
5945 if( zIn < zEnd && zIn[0] == ':' ){
5946 zIn++;
5947 need_val = 1;
5948 if( zIn < zEnd && zIn[0] == ':' ){
5949 zIn++;
5950 }
5951 }
5952 /* Find option */
5953 zArg = VmFindShortOpt(c, zArgIn, zArgEnd);
5954 if( zArg == 0 ){
5955 /* No such option */
5956 continue;
5957 }
5958 /* Extract option argument value */
5959 VmExtractOptArgValue(pArray, pWorker, zArg, zArgEnd, need_val, pCtx, (const char *)&c);
5960 }
5961 if( nArg > 1 && jx9_value_is_json_array(apArg[1]) && jx9_array_count(apArg[1]) > 0 ){
5962 /* Process long options */
5963 jx9_array_walk(apArg[1], VmProcessLongOpt, &sLong);
5964 }
5965 /* Return the option array */
5966 jx9_result_value(pCtx, pArray);
5967 /*
5968 * Don't worry about freeing memory, everything will be released
5969 * automatically as soon we return from this foreign function.
5970 */
5971 return JX9_OK;
5972}
5973/*
5974 * Array walker callback used for processing long options values.
5975 */
5976static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData)
5977{
5978 struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData;
5979 const char *zArg, *zOpt, *zEnd;
5980 int need_value = 0;
5981 int nByte;
5982 /* Value must be of type string */
5983 if( !jx9_value_is_string(pValue) ){
5984 /* Simply ignore */
5985 return JX9_OK;
5986 }
5987 zOpt = jx9_value_to_string(pValue, &nByte);
5988 if( nByte < 1 ){
5989 /* Empty string, ignore */
5990 return JX9_OK;
5991 }
5992 zEnd = &zOpt[nByte - 1];
5993 if( zEnd[0] == ':' ){
5994 char *zTerm;
5995 /* Try to extract a value */
5996 need_value = 1;
5997 while( zEnd >= zOpt && zEnd[0] == ':' ){
5998 zEnd--;
5999 }
6000 if( zOpt >= zEnd ){
6001 /* Empty string, ignore */
6002 SXUNUSED(pKey);
6003 return JX9_OK;
6004 }
6005 zEnd++;
6006 zTerm = (char *)zEnd;
6007 zTerm[0] = 0;
6008 }else{
6009 zEnd = &zOpt[nByte];
6010 }
6011 /* Find the option */
6012 zArg = VmFindLongOpt(zOpt, (int)(zEnd-zOpt), pOpt->zArgIn, pOpt->zArgEnd);
6013 if( zArg == 0 ){
6014 /* No such option, return immediately */
6015 return JX9_OK;
6016 }
6017 /* Try to extract a value */
6018 VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt);
6019 return JX9_OK;
6020}
6021/*
6022 * int utf8_encode(string $input)
6023 * UTF-8 encoding.
6024 * This function encodes the string data to UTF-8, and returns the encoded version.
6025 * UTF-8 is a standard mechanism used by Unicode for encoding wide character values
6026 * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized
6027 * (meaning it is possible for a program to figure out where in the bytestream characters start)
6028 * and can be used with normal string comparison functions for sorting and such.
6029 * Notes on UTF-8 (According to SQLite3 authors):
6030 * Byte-0 Byte-1 Byte-2 Byte-3 Value
6031 * 0xxxxxxx 00000000 00000000 0xxxxxxx
6032 * 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
6033 * 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
6034 * 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx
6035 * Parameters
6036 * $input
6037 * String to encode or NULL on failure.
6038 * Return
6039 * An UTF-8 encoded string.
6040 */
6041static int vm_builtin_utf8_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
6042{
6043 const unsigned char *zIn, *zEnd;
6044 int nByte, c, e;
6045 if( nArg < 1 ){
6046 /* Missing arguments, return null */
6047 jx9_result_null(pCtx);
6048 return JX9_OK;
6049 }
6050 /* Extract the target string */
6051 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
6052 if( nByte < 1 ){
6053 /* Empty string, return null */
6054 jx9_result_null(pCtx);
6055 return JX9_OK;
6056 }
6057 zEnd = &zIn[nByte];
6058 /* Start the encoding process */
6059 for(;;){
6060 if( zIn >= zEnd ){
6061 /* End of input */
6062 break;
6063 }
6064 c = zIn[0];
6065 /* Advance the stream cursor */
6066 zIn++;
6067 /* Encode */
6068 if( c<0x00080 ){
6069 e = (c&0xFF);
6070 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6071 }else if( c<0x00800 ){
6072 e = 0xC0 + ((c>>6)&0x1F);
6073 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6074 e = 0x80 + (c & 0x3F);
6075 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6076 }else if( c<0x10000 ){
6077 e = 0xE0 + ((c>>12)&0x0F);
6078 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6079 e = 0x80 + ((c>>6) & 0x3F);
6080 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6081 e = 0x80 + (c & 0x3F);
6082 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6083 }else{
6084 e = 0xF0 + ((c>>18) & 0x07);
6085 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6086 e = 0x80 + ((c>>12) & 0x3F);
6087 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6088 e = 0x80 + ((c>>6) & 0x3F);
6089 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6090 e = 0x80 + (c & 0x3F);
6091 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
6092 }
6093 }
6094 /* All done */
6095 return JX9_OK;
6096}
6097/*
6098 * UTF-8 decoding routine extracted from the sqlite3 source tree.
6099 * Original author: D. Richard Hipp (http://www.sqlite.org)
6100 * Status: Public Domain
6101 */
6102/*
6103** This lookup table is used to help decode the first byte of
6104** a multi-byte UTF8 character.
6105*/
6106static const unsigned char UtfTrans1[] = {
6107 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
6108 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
6109 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
6110 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
6111 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
6112 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
6113 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
6114 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
6115};
6116/*
6117** Translate a single UTF-8 character. Return the unicode value.
6118**
6119** During translation, assume that the byte that zTerm points
6120** is a 0x00.
6121**
6122** Write a pointer to the next unread byte back into *pzNext.
6123**
6124** Notes On Invalid UTF-8:
6125**
6126** * This routine never allows a 7-bit character (0x00 through 0x7f) to
6127** be encoded as a multi-byte character. Any multi-byte character that
6128** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
6129**
6130** * This routine never allows a UTF16 surrogate value to be encoded.
6131** If a multi-byte character attempts to encode a value between
6132** 0xd800 and 0xe000 then it is rendered as 0xfffd.
6133**
6134** * Bytes in the range of 0x80 through 0xbf which occur as the first
6135** byte of a character are interpreted as single-byte characters
6136** and rendered as themselves even though they are technically
6137** invalid characters.
6138**
6139** * This routine accepts an infinite number of different UTF8 encodings
6140** for unicode values 0x80 and greater. It do not change over-length
6141** encodings to 0xfffd as some systems recommend.
6142*/
6143#define READ_UTF8(zIn, zTerm, c) \
6144 c = *(zIn++); \
6145 if( c>=0xc0 ){ \
6146 c = UtfTrans1[c-0xc0]; \
6147 while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
6148 c = (c<<6) + (0x3f & *(zIn++)); \
6149 } \
6150 if( c<0x80 \
6151 || (c&0xFFFFF800)==0xD800 \
6152 || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
6153 }
6154JX9_PRIVATE int jx9Utf8Read(
6155 const unsigned char *z, /* First byte of UTF-8 character */
6156 const unsigned char *zTerm, /* Pretend this byte is 0x00 */
6157 const unsigned char **pzNext /* Write first byte past UTF-8 char here */
6158){
6159 int c;
6160 READ_UTF8(z, zTerm, c);
6161 *pzNext = z;
6162 return c;
6163}
6164/*
6165 * string utf8_decode(string $data)
6166 * This function decodes data, assumed to be UTF-8 encoded, to unicode.
6167 * Parameters
6168 * data
6169 * An UTF-8 encoded string.
6170 * Return
6171 * Unicode decoded string or NULL on failure.
6172 */
6173static int vm_builtin_utf8_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
6174{
6175 const unsigned char *zIn, *zEnd;
6176 int nByte, c;
6177 if( nArg < 1 ){
6178 /* Missing arguments, return null */
6179 jx9_result_null(pCtx);
6180 return JX9_OK;
6181 }
6182 /* Extract the target string */
6183 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
6184 if( nByte < 1 ){
6185 /* Empty string, return null */
6186 jx9_result_null(pCtx);
6187 return JX9_OK;
6188 }
6189 zEnd = &zIn[nByte];
6190 /* Start the decoding process */
6191 while( zIn < zEnd ){
6192 c = jx9Utf8Read(zIn, zEnd, &zIn);
6193 if( c == 0x0 ){
6194 break;
6195 }
6196 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
6197 }
6198 return JX9_OK;
6199}
6200/*
6201 * string json_encode(mixed $value)
6202 * Returns a string containing the JSON representation of value.
6203 * Parameters
6204 * $value
6205 * The value being encoded. Can be any type except a resource.
6206 * Return
6207 * Returns a JSON encoded string on success. FALSE otherwise
6208 */
6209static int vm_builtin_json_encode(jx9_context *pCtx,int nArg,jx9_value **apArg)
6210{
6211 SyBlob sBlob;
6212 if( nArg < 1 ){
6213 /* Missing arguments, return FALSE */
6214 jx9_result_bool(pCtx, 0);
6215 return JX9_OK;
6216 }
6217 /* Init the working buffer */
6218 SyBlobInit(&sBlob,&pCtx->pVm->sAllocator);
6219 /* Perform the encoding operation */
6220 jx9JsonSerialize(apArg[0],&sBlob);
6221 /* Return the serialized value */
6222 jx9_result_string(pCtx,(const char *)SyBlobData(&sBlob),(int)SyBlobLength(&sBlob));
6223 /* Cleanup */
6224 SyBlobRelease(&sBlob);
6225 /* All done */
6226 return JX9_OK;
6227}
6228/*
6229 * mixed json_decode(string $json)
6230 * Takes a JSON encoded string and converts it into a JX9 variable.
6231 * Parameters
6232 * $json
6233 * The json string being decoded.
6234 * Return
6235 * The value encoded in json in appropriate JX9 type. Values true, false and null (case-insensitive)
6236 * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded
6237 * or if the encoded data is deeper than the recursion limit.
6238 */
6239static int vm_builtin_json_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
6240{
6241 const char *zJSON;
6242 int nByte;
6243 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
6244 /* Missing/Invalid arguments, return NULL */
6245 jx9_result_null(pCtx);
6246 return JX9_OK;
6247 }
6248 /* Extract the JSON string */
6249 zJSON = jx9_value_to_string(apArg[0], &nByte);
6250 if( nByte < 1 ){
6251 /* Empty string, return NULL */
6252 jx9_result_null(pCtx);
6253 return JX9_OK;
6254 }
6255 /* Decode the raw JSON */
6256 jx9JsonDecode(pCtx,zJSON,nByte);
6257 return JX9_OK;
6258}
6259/* Table of built-in VM functions. */
6260static const jx9_builtin_func aVmFunc[] = {
6261 /* JSON Encoding/Decoding */
6262 { "json_encode", vm_builtin_json_encode },
6263 { "json_decode", vm_builtin_json_decode },
6264 /* Functions calls */
6265 { "func_num_args" , vm_builtin_func_num_args },
6266 { "func_get_arg" , vm_builtin_func_get_arg },
6267 { "func_get_args" , vm_builtin_func_get_args },
6268 { "function_exists", vm_builtin_func_exists },
6269 { "is_callable" , vm_builtin_is_callable },
6270 { "get_defined_functions", vm_builtin_get_defined_func },
6271 /* Constants management */
6272 { "defined", vm_builtin_defined },
6273 { "get_defined_constants", vm_builtin_get_defined_constants },
6274 /* Random numbers/strings generators */
6275 { "rand", vm_builtin_rand },
6276 { "rand_str", vm_builtin_rand_str },
6277 { "getrandmax", vm_builtin_getrandmax },
6278 /* Language constructs functions */
6279 { "print", vm_builtin_print },
6280 { "exit", vm_builtin_exit },
6281 { "die", vm_builtin_exit },
6282 /* Variable handling functions */
6283 { "gettype", vm_builtin_gettype },
6284 { "get_resource_type", vm_builtin_get_resource_type},
6285 /* Variable dumping */
6286 { "dump", vm_builtin_dump },
6287 /* Release info */
6288 {"jx9_version", vm_builtin_jx9_version },
6289 {"jx9_credits", vm_builtin_jx9_version },
6290 {"jx9_info", vm_builtin_jx9_version },
6291 {"jx9_copyright", vm_builtin_jx9_version },
6292 /* hashmap */
6293 {"extract", vm_builtin_extract },
6294 /* URL related function */
6295 {"parse_url", vm_builtin_parse_url },
6296 /* UTF-8 encoding/decoding */
6297 {"utf8_encode", vm_builtin_utf8_encode},
6298 {"utf8_decode", vm_builtin_utf8_decode},
6299 /* Command line processing */
6300 {"getopt", vm_builtin_getopt },
6301 /* Files/URI inclusion facility */
6302 { "include", vm_builtin_include },
6303 { "import", vm_builtin_import }
6304};
6305/*
6306 * Register the built-in VM functions defined above.
6307 */
6308static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm)
6309{
6310 sxi32 rc;
6311 sxu32 n;
6312 for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){
6313 /* Note that these special functions have access
6314 * to the underlying virtual machine as their
6315 * private data.
6316 */
6317 rc = jx9_create_function(&(*pVm), aVmFunc[n].zName, aVmFunc[n].xFunc, &(*pVm));
6318 if( rc != SXRET_OK ){
6319 return rc;
6320 }
6321 }
6322 return SXRET_OK;
6323}
6324#ifndef JX9_DISABLE_BUILTIN_FUNC
6325/*
6326 * Extract the IO stream device associated with a given scheme.
6327 * Return a pointer to an instance of jx9_io_stream when the scheme
6328 * have an associated IO stream registered with it. NULL otherwise.
6329 * If no scheme:// is avalilable then the file:// scheme is assumed.
6330 * For more information on how to register IO stream devices, please
6331 * refer to the official documentation.
6332 */
6333JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(
6334 jx9_vm *pVm, /* Target VM */
6335 const char **pzDevice, /* Full path, URI, ... */
6336 int nByte /* *pzDevice length*/
6337 )
6338{
6339 const char *zIn, *zEnd, *zCur, *zNext;
6340 jx9_io_stream **apStream, *pStream;
6341 SyString sDev, sCur;
6342 sxu32 n, nEntry;
6343 int rc;
6344 /* Check if a scheme [i.e: file://, http://, zip://...] is available */
6345 zNext = zCur = zIn = *pzDevice;
6346 zEnd = &zIn[nByte];
6347 while( zIn < zEnd ){
6348 if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){
6349 /* Got one */
6350 zNext = &zIn[sizeof("://")-1];
6351 break;
6352 }
6353 /* Advance the cursor */
6354 zIn++;
6355 }
6356 if( zIn >= zEnd ){
6357 /* No such scheme, return the default stream */
6358 return pVm->pDefStream;
6359 }
6360 SyStringInitFromBuf(&sDev, zCur, zIn-zCur);
6361 /* Remove leading and trailing white spaces */
6362 SyStringFullTrim(&sDev);
6363 /* Perform a linear lookup on the installed stream devices */
6364 apStream = (jx9_io_stream **)SySetBasePtr(&pVm->aIOstream);
6365 nEntry = SySetUsed(&pVm->aIOstream);
6366 for( n = 0 ; n < nEntry ; n++ ){
6367 pStream = apStream[n];
6368 SyStringInitFromBuf(&sCur, pStream->zName, SyStrlen(pStream->zName));
6369 /* Perfrom a case-insensitive comparison */
6370 rc = SyStringCmp(&sDev, &sCur, SyStrnicmp);
6371 if( rc == 0 ){
6372 /* Stream device found */
6373 *pzDevice = zNext;
6374 return pStream;
6375 }
6376 }
6377 /* No such stream, return NULL */
6378 return 0;
6379}
6380#endif /* JX9_DISABLE_BUILTIN_FUNC */
6381/*
6382 * Section:
6383 * HTTP/URI related routines.
6384 * Authors:
6385 * Symisc Systems, devel@symisc.net.
6386 * Copyright (C) Symisc Systems, http://jx9.symisc.net
6387 * Status:
6388 * Stable.
6389 */
6390 /*
6391 * URI Parser: Split an URI into components [i.e: Host, Path, Query, ...].
6392 * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document]
6393 * This almost, but not quite, RFC1738 URI syntax.
6394 * This routine is not a validator, it does not check for validity
6395 * nor decode URI parts, the only thing this routine does is splitting
6396 * the input to its fields.
6397 * Upper layer are responsible of decoding and validating URI parts.
6398 * On success, this function populate the "SyhttpUri" structure passed
6399 * as the first argument. Otherwise SXERR_* is returned when a malformed
6400 * input is encountered.
6401 */
6402 static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen)
6403 {
6404 const char *zEnd = &zUri[nLen];
6405 sxu8 bHostOnly = FALSE;
6406 sxu8 bIPv6 = FALSE ;
6407 const char *zCur;
6408 SyString *pComp;
6409 sxu32 nPos = 0;
6410 sxi32 rc;
6411 /* Zero the structure first */
6412 SyZero(pOut, sizeof(SyhttpUri));
6413 /* Remove leading and trailing white spaces */
6414 SyStringInitFromBuf(&pOut->sRaw, zUri, nLen);
6415 SyStringFullTrim(&pOut->sRaw);
6416 /* Find the first '/' separator */
6417 rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
6418 if( rc != SXRET_OK ){
6419 /* Assume a host name only */
6420 zCur = zEnd;
6421 bHostOnly = TRUE;
6422 goto ProcessHost;
6423 }
6424 zCur = &zUri[nPos];
6425 if( zUri != zCur && zCur[-1] == ':' ){
6426 /* Extract a scheme:
6427 * Not that we can get an invalid scheme here.
6428 * Fortunately the caller can discard any URI by comparing this scheme with its
6429 * registered schemes and will report the error as soon as his comparison function
6430 * fail.
6431 */
6432 pComp = &pOut->sScheme;
6433 SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri - 1));
6434 SyStringLeftTrim(pComp);
6435 }
6436 if( zCur[1] != '/' ){
6437 if( zCur == zUri || zCur[-1] == ':' ){
6438 /* No authority */
6439 goto PathSplit;
6440 }
6441 /* There is something here , we will assume its an authority
6442 * and someone has forgot the two prefix slashes "//",
6443 * sooner or later we will detect if we are dealing with a malicious
6444 * user or not, but now assume we are dealing with an authority
6445 * and let the caller handle all the validation process.
6446 */
6447 goto ProcessHost;
6448 }
6449 zUri = &zCur[2];
6450 zCur = zEnd;
6451 rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
6452 if( rc == SXRET_OK ){
6453 zCur = &zUri[nPos];
6454 }
6455 ProcessHost:
6456 /* Extract user information if present */
6457 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), '@', &nPos);
6458 if( rc == SXRET_OK ){
6459 if( nPos > 0 ){
6460 sxu32 nPassOfft; /* Password offset */
6461 pComp = &pOut->sUser;
6462 SyStringInitFromBuf(pComp, zUri, nPos);
6463 /* Extract the password if available */
6464 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPassOfft);
6465 if( rc == SXRET_OK && nPassOfft < nPos){
6466 pComp->nByte = nPassOfft;
6467 pComp = &pOut->sPass;
6468 pComp->zString = &zUri[nPassOfft+sizeof(char)];
6469 pComp->nByte = nPos - nPassOfft - 1;
6470 }
6471 /* Update the cursor */
6472 zUri = &zUri[nPos+1];
6473 }else{
6474 zUri++;
6475 }
6476 }
6477 pComp = &pOut->sHost;
6478 while( zUri < zCur && SyisSpace(zUri[0])){
6479 zUri++;
6480 }
6481 SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri));
6482 if( pComp->zString[0] == '[' ){
6483 /* An IPv6 Address: Make a simple naive test
6484 */
6485 zUri++; pComp->zString++; pComp->nByte = 0;
6486 while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){
6487 zUri++; pComp->nByte++;
6488 }
6489 if( zUri[0] != ']' ){
6490 return SXERR_CORRUPT; /* Malformed IPv6 address */
6491 }
6492 zUri++;
6493 bIPv6 = TRUE;
6494 }
6495 /* Extract a port number if available */
6496 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPos);
6497 if( rc == SXRET_OK ){
6498 if( bIPv6 == FALSE ){
6499 pComp->nByte = (sxu32)(&zUri[nPos] - zUri);
6500 }
6501 pComp = &pOut->sPort;
6502 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zCur - &zUri[nPos+1]));
6503 }
6504 if( bHostOnly == TRUE ){
6505 return SXRET_OK;
6506 }
6507PathSplit:
6508 zUri = zCur;
6509 pComp = &pOut->sPath;
6510 SyStringInitFromBuf(pComp, zUri, (sxu32)(zEnd-zUri));
6511 if( pComp->nByte == 0 ){
6512 return SXRET_OK; /* Empty path */
6513 }
6514 if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '?', &nPos) ){
6515 pComp->nByte = nPos; /* Update path length */
6516 pComp = &pOut->sQuery;
6517 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]));
6518 }
6519 if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '#', &nPos) ){
6520 /* Update path or query length */
6521 if( pComp == &pOut->sPath ){
6522 pComp->nByte = nPos;
6523 }else{
6524 if( &zUri[nPos] < (char *)SyStringData(pComp) ){
6525 /* Malformed syntax : Query must be present before fragment */
6526 return SXERR_SYNTAX;
6527 }
6528 pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]);
6529 }
6530 pComp = &pOut->sFragment;
6531 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]))
6532 }
6533 return SXRET_OK;
6534 }
6535 /*
6536 * Extract a single line from a raw HTTP request.
6537 * Return SXRET_OK on success, SXERR_EOF when end of input
6538 * and SXERR_MORE when more input is needed.
6539 */
6540static sxi32 VmGetNextLine(SyString *pCursor, SyString *pCurrent)
6541{
6542 const char *zIn;
6543 sxu32 nPos;
6544 /* Jump leading white spaces */
6545 SyStringLeftTrim(pCursor);
6546 if( pCursor->nByte < 1 ){
6547 SyStringInitFromBuf(pCurrent, 0, 0);
6548 return SXERR_EOF; /* End of input */
6549 }
6550 zIn = SyStringData(pCursor);
6551 if( SXRET_OK != SyByteListFind(pCursor->zString, pCursor->nByte, "\r\n", &nPos) ){
6552 /* Line not found, tell the caller to read more input from source */
6553 SyStringDupPtr(pCurrent, pCursor);
6554 return SXERR_MORE;
6555 }
6556 pCurrent->zString = zIn;
6557 pCurrent->nByte = nPos;
6558 /* advance the cursor so we can call this routine again */
6559 pCursor->zString = &zIn[nPos];
6560 pCursor->nByte -= nPos;
6561 return SXRET_OK;
6562 }
6563 /*
6564 * Split a single MIME header into a name value pair.
6565 * This function return SXRET_OK, SXERR_CONTINUE on success.
6566 * Otherwise SXERR_NEXT is returned when a malformed header
6567 * is encountered.
6568 * Note: This function handle also mult-line headers.
6569 */
6570 static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr, SyhttpHeader *pLast, const char *zLine, sxu32 nLen)
6571 {
6572 SyString *pName;
6573 sxu32 nPos;
6574 sxi32 rc;
6575 if( nLen < 1 ){
6576 return SXERR_NEXT;
6577 }
6578 /* Check for multi-line header */
6579 if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){
6580 SyString *pTmp = &pLast->sValue;
6581 SyStringFullTrim(pTmp);
6582 if( pTmp->nByte == 0 ){
6583 SyStringInitFromBuf(pTmp, zLine, nLen);
6584 }else{
6585 /* Update header value length */
6586 pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString);
6587 }
6588 /* Simply tell the caller to reset its states and get another line */
6589 return SXERR_CONTINUE;
6590 }
6591 /* Split the header */
6592 pName = &pHdr->sName;
6593 rc = SyByteFind(zLine, nLen, ':', &nPos);
6594 if(rc != SXRET_OK ){
6595 return SXERR_NEXT; /* Malformed header;Check the next entry */
6596 }
6597 SyStringInitFromBuf(pName, zLine, nPos);
6598 SyStringFullTrim(pName);
6599 /* Extract a header value */
6600 SyStringInitFromBuf(&pHdr->sValue, &zLine[nPos + 1], nLen - nPos - 1);
6601 /* Remove leading and trailing whitespaces */
6602 SyStringFullTrim(&pHdr->sValue);
6603 return SXRET_OK;
6604 }
6605 /*
6606 * Extract all MIME headers associated with a HTTP request.
6607 * After processing the first line of a HTTP request, the following
6608 * routine is called in order to extract MIME headers.
6609 * This function return SXRET_OK on success, SXERR_MORE when it needs
6610 * more inputs.
6611 * Note: Any malformed header is simply discarded.
6612 */
6613 static sxi32 VmHttpExtractHeaders(SyString *pRequest, SySet *pOut)
6614 {
6615 SyhttpHeader *pLast = 0;
6616 SyString sCurrent;
6617 SyhttpHeader sHdr;
6618 sxu8 bEol;
6619 sxi32 rc;
6620 if( SySetUsed(pOut) > 0 ){
6621 pLast = (SyhttpHeader *)SySetAt(pOut, SySetUsed(pOut)-1);
6622 }
6623 bEol = FALSE;
6624 for(;;){
6625 SyZero(&sHdr, sizeof(SyhttpHeader));
6626 /* Extract a single line from the raw HTTP request */
6627 rc = VmGetNextLine(pRequest, &sCurrent);
6628 if(rc != SXRET_OK ){
6629 if( sCurrent.nByte < 1 ){
6630 break;
6631 }
6632 bEol = TRUE;
6633 }
6634 /* Process the header */
6635 if( SXRET_OK == VmHttpProcessOneHeader(&sHdr, pLast, sCurrent.zString, sCurrent.nByte)){
6636 if( SXRET_OK != SySetPut(pOut, (const void *)&sHdr) ){
6637 break;
6638 }
6639 /* Retrieve the last parsed header so we can handle multi-line header
6640 * in case we face one of them.
6641 */
6642 pLast = (SyhttpHeader *)SySetPeek(pOut);
6643 }
6644 if( bEol ){
6645 break;
6646 }
6647 } /* for(;;) */
6648 return SXRET_OK;
6649 }
6650 /*
6651 * Process the first line of a HTTP request.
6652 * This routine perform the following operations
6653 * 1) Extract the HTTP method.
6654 * 2) Split the request URI to it's fields [ie: host, path, query, ...].
6655 * 3) Extract the HTTP protocol version.
6656 */
6657 static sxi32 VmHttpProcessFirstLine(
6658 SyString *pRequest, /* Raw HTTP request */
6659 sxi32 *pMethod, /* OUT: HTTP method */
6660 SyhttpUri *pUri, /* OUT: Parse of the URI */
6661 sxi32 *pProto /* OUT: HTTP protocol */
6662 )
6663 {
6664 static const char *azMethods[] = { "get", "post", "head", "put"};
6665 static const sxi32 aMethods[] = { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD, HTTP_METHOD_PUT};
6666 const char *zIn, *zEnd, *zPtr;
6667 SyString sLine;
6668 sxu32 nLen;
6669 sxi32 rc;
6670 /* Extract the first line and update the pointer */
6671 rc = VmGetNextLine(pRequest, &sLine);
6672 if( rc != SXRET_OK ){
6673 return rc;
6674 }
6675 if ( sLine.nByte < 1 ){
6676 /* Empty HTTP request */
6677 return SXERR_EMPTY;
6678 }
6679 /* Delimit the line and ignore trailing and leading white spaces */
6680 zIn = sLine.zString;
6681 zEnd = &zIn[sLine.nByte];
6682 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
6683 zIn++;
6684 }
6685 /* Extract the HTTP method */
6686 zPtr = zIn;
6687 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
6688 zIn++;
6689 }
6690 *pMethod = HTTP_METHOD_OTHR;
6691 if( zIn > zPtr ){
6692 sxu32 i;
6693 nLen = (sxu32)(zIn-zPtr);
6694 for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){
6695 if( SyStrnicmp(azMethods[i], zPtr, nLen) == 0 ){
6696 *pMethod = aMethods[i];
6697 break;
6698 }
6699 }
6700 }
6701 /* Jump trailing white spaces */
6702 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
6703 zIn++;
6704 }
6705 /* Extract the request URI */
6706 zPtr = zIn;
6707 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
6708 zIn++;
6709 }
6710 if( zIn > zPtr ){
6711 nLen = (sxu32)(zIn-zPtr);
6712 /* Split raw URI to it's fields */
6713 VmHttpSplitURI(pUri, zPtr, nLen);
6714 }
6715 /* Jump trailing white spaces */
6716 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
6717 zIn++;
6718 }
6719 /* Extract the HTTP version */
6720 zPtr = zIn;
6721 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
6722 zIn++;
6723 }
6724 *pProto = HTTP_PROTO_11; /* HTTP/1.1 */
6725 rc = 1;
6726 if( zIn > zPtr ){
6727 rc = SyStrnicmp(zPtr, "http/1.0", (sxu32)(zIn-zPtr));
6728 }
6729 if( !rc ){
6730 *pProto = HTTP_PROTO_10; /* HTTP/1.0 */
6731 }
6732 return SXRET_OK;
6733 }
6734 /*
6735 * Tokenize, decode and split a raw query encoded as: "x-www-form-urlencoded"
6736 * into a name value pair.
6737 * Note that this encoding is implicit in GET based requests.
6738 * After the tokenization process, register the decoded queries
6739 * in the $_GET/$_POST/$_REQUEST superglobals arrays.
6740 */
6741 static sxi32 VmHttpSplitEncodedQuery(
6742 jx9_vm *pVm, /* Target VM */
6743 SyString *pQuery, /* Raw query to decode */
6744 SyBlob *pWorker, /* Working buffer */
6745 int is_post /* TRUE if we are dealing with a POST request */
6746 )
6747 {
6748 const char *zEnd = &pQuery->zString[pQuery->nByte];
6749 const char *zIn = pQuery->zString;
6750 jx9_value *pGet, *pRequest;
6751 SyString sName, sValue;
6752 const char *zPtr;
6753 sxu32 nBlobOfft;
6754 /* Extract superglobals */
6755 if( is_post ){
6756 /* $_POST superglobal */
6757 pGet = VmExtractSuper(&(*pVm), "_POST", sizeof("_POST")-1);
6758 }else{
6759 /* $_GET superglobal */
6760 pGet = VmExtractSuper(&(*pVm), "_GET", sizeof("_GET")-1);
6761 }
6762 pRequest = VmExtractSuper(&(*pVm), "_REQUEST", sizeof("_REQUEST")-1);
6763 /* Split up the raw query */
6764 for(;;){
6765 /* Jump leading white spaces */
6766 while(zIn < zEnd && SyisSpace(zIn[0]) ){
6767 zIn++;
6768 }
6769 if( zIn >= zEnd ){
6770 break;
6771 }
6772 zPtr = zIn;
6773 while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){
6774 zPtr++;
6775 }
6776 /* Reset the working buffer */
6777 SyBlobReset(pWorker);
6778 /* Decode the entry */
6779 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
6780 /* Save the entry */
6781 sName.nByte = SyBlobLength(pWorker);
6782 sValue.zString = 0;
6783 sValue.nByte = 0;
6784 if( zPtr < zEnd && zPtr[0] == '=' ){
6785 zPtr++;
6786 zIn = zPtr;
6787 /* Store field value */
6788 while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){
6789 zPtr++;
6790 }
6791 if( zPtr > zIn ){
6792 /* Decode the value */
6793 nBlobOfft = SyBlobLength(pWorker);
6794 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
6795 sValue.zString = (const char *)SyBlobDataAt(pWorker, nBlobOfft);
6796 sValue.nByte = SyBlobLength(pWorker) - nBlobOfft;
6797
6798 }
6799 /* Synchronize pointers */
6800 zIn = zPtr;
6801 }
6802 sName.zString = (const char *)SyBlobData(pWorker);
6803 /* Install the decoded query in the $_GET/$_REQUEST array */
6804 if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){
6805 VmHashmapInsert((jx9_hashmap *)pGet->x.pOther,
6806 sName.zString, (int)sName.nByte,
6807 sValue.zString, (int)sValue.nByte
6808 );
6809 }
6810 if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){
6811 VmHashmapInsert((jx9_hashmap *)pRequest->x.pOther,
6812 sName.zString, (int)sName.nByte,
6813 sValue.zString, (int)sValue.nByte
6814 );
6815 }
6816 /* Advance the pointer */
6817 zIn = &zPtr[1];
6818 }
6819 /* All done*/
6820 return SXRET_OK;
6821 }
6822 /*
6823 * Extract MIME header value from the given set.
6824 * Return header value on success. NULL otherwise.
6825 */
6826 static SyString * VmHttpExtractHeaderValue(SySet *pSet, const char *zMime, sxu32 nByte)
6827 {
6828 SyhttpHeader *aMime, *pMime;
6829 SyString sMime;
6830 sxu32 n;
6831 SyStringInitFromBuf(&sMime, zMime, nByte);
6832 /* Point to the MIME entries */
6833 aMime = (SyhttpHeader *)SySetBasePtr(pSet);
6834 /* Perform the lookup */
6835 for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
6836 pMime = &aMime[n];
6837 if( SyStringCmp(&sMime, &pMime->sName, SyStrnicmp) == 0 ){
6838 /* Header found, return it's associated value */
6839 return &pMime->sValue;
6840 }
6841 }
6842 /* No such MIME header */
6843 return 0;
6844 }
6845 /*
6846 * Tokenize and decode a raw "Cookie:" MIME header into a name value pair
6847 * and insert it's fields [i.e name, value] in the $_COOKIE superglobal.
6848 */
6849 static sxi32 VmHttpPorcessCookie(jx9_vm *pVm, SyBlob *pWorker, const char *zIn, sxu32 nByte)
6850 {
6851 const char *zPtr, *zDelimiter, *zEnd = &zIn[nByte];
6852 SyString sName, sValue;
6853 jx9_value *pCookie;
6854 sxu32 nOfft;
6855 /* Make sure the $_COOKIE superglobal is available */
6856 pCookie = VmExtractSuper(&(*pVm), "_COOKIE", sizeof("_COOKIE")-1);
6857 if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){
6858 /* $_COOKIE superglobal not available */
6859 return SXERR_NOTFOUND;
6860 }
6861 for(;;){
6862 /* Jump leading white spaces */
6863 while( zIn < zEnd && SyisSpace(zIn[0]) ){
6864 zIn++;
6865 }
6866 if( zIn >= zEnd ){
6867 break;
6868 }
6869 /* Reset the working buffer */
6870 SyBlobReset(pWorker);
6871 zDelimiter = zIn;
6872 /* Delimit the name[=value]; pair */
6873 while( zDelimiter < zEnd && zDelimiter[0] != ';' ){
6874 zDelimiter++;
6875 }
6876 zPtr = zIn;
6877 while( zPtr < zDelimiter && zPtr[0] != '=' ){
6878 zPtr++;
6879 }
6880 /* Decode the cookie */
6881 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
6882 sName.nByte = SyBlobLength(pWorker);
6883 zPtr++;
6884 sValue.zString = 0;
6885 sValue.nByte = 0;
6886 if( zPtr < zDelimiter ){
6887 /* Got a Cookie value */
6888 nOfft = SyBlobLength(pWorker);
6889 SyUriDecode(zPtr, (sxu32)(zDelimiter-zPtr), jx9VmBlobConsumer, pWorker, TRUE);
6890 SyStringInitFromBuf(&sValue, SyBlobDataAt(pWorker, nOfft), SyBlobLength(pWorker)-nOfft);
6891 }
6892 /* Synchronize pointers */
6893 zIn = &zDelimiter[1];
6894 /* Perform the insertion */
6895 sName.zString = (const char *)SyBlobData(pWorker);
6896 VmHashmapInsert((jx9_hashmap *)pCookie->x.pOther,
6897 sName.zString, (int)sName.nByte,
6898 sValue.zString, (int)sValue.nByte
6899 );
6900 }
6901 return SXRET_OK;
6902 }
6903 /*
6904 * Process a full HTTP request and populate the appropriate arrays
6905 * such as $_SERVER, $_GET, $_POST, $_COOKIE, $_REQUEST, ... with the information
6906 * extracted from the raw HTTP request. As an extension Symisc introduced
6907 * the $_HEADER array which hold a copy of the processed HTTP MIME headers
6908 * and their associated values. [i.e: $_HEADER['Server'], $_HEADER['User-Agent'], ...].
6909 * This function return SXRET_OK on success. Any other return value indicates
6910 * a malformed HTTP request.
6911 */
6912 static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte)
6913 {
6914 SyString *pName, *pValue, sRequest; /* Raw HTTP request */
6915 jx9_value *pHeaderArray; /* $_HEADER superglobal (Symisc eXtension to the JX9 specification)*/
6916 SyhttpHeader *pHeader; /* MIME header */
6917 SyhttpUri sUri; /* Parse of the raw URI*/
6918 SyBlob sWorker; /* General purpose working buffer */
6919 SySet sHeader; /* MIME headers set */
6920 sxi32 iMethod; /* HTTP method [i.e: GET, POST, HEAD...]*/
6921 sxi32 iVer; /* HTTP protocol version */
6922 sxi32 rc;
6923 SyStringInitFromBuf(&sRequest, zRequest, nByte);
6924 SySetInit(&sHeader, &pVm->sAllocator, sizeof(SyhttpHeader));
6925 SyBlobInit(&sWorker, &pVm->sAllocator);
6926 /* Ignore leading and trailing white spaces*/
6927 SyStringFullTrim(&sRequest);
6928 /* Process the first line */
6929 rc = VmHttpProcessFirstLine(&sRequest, &iMethod, &sUri, &iVer);
6930 if( rc != SXRET_OK ){
6931 return rc;
6932 }
6933 /* Process MIME headers */
6934 VmHttpExtractHeaders(&sRequest, &sHeader);
6935 /*
6936 * Setup $_SERVER environments
6937 */
6938 /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */
6939 jx9_vm_config(pVm,
6940 JX9_VM_CONFIG_SERVER_ATTR,
6941 "SERVER_PROTOCOL",
6942 iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1",
6943 sizeof("HTTP/1.1")-1
6944 );
6945 /* 'REQUEST_METHOD': Which request method was used to access the page */
6946 jx9_vm_config(pVm,
6947 JX9_VM_CONFIG_SERVER_ATTR,
6948 "REQUEST_METHOD",
6949 iMethod == HTTP_METHOD_GET ? "GET" :
6950 (iMethod == HTTP_METHOD_POST ? "POST":
6951 (iMethod == HTTP_METHOD_PUT ? "PUT" :
6952 (iMethod == HTTP_METHOD_HEAD ? "HEAD" : "OTHER"))),
6953 -1 /* Compute attribute length automatically */
6954 );
6955 if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){
6956 pValue = &sUri.sQuery;
6957 /* 'QUERY_STRING': The query string, if any, via which the page was accessed */
6958 jx9_vm_config(pVm,
6959 JX9_VM_CONFIG_SERVER_ATTR,
6960 "QUERY_STRING",
6961 pValue->zString,
6962 pValue->nByte
6963 );
6964 /* Decoded the raw query */
6965 VmHttpSplitEncodedQuery(&(*pVm), pValue, &sWorker, FALSE);
6966 }
6967 /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */
6968 pValue = &sUri.sRaw;
6969 jx9_vm_config(pVm,
6970 JX9_VM_CONFIG_SERVER_ATTR,
6971 "REQUEST_URI",
6972 pValue->zString,
6973 pValue->nByte
6974 );
6975 /*
6976 * 'PATH_INFO'
6977 * 'ORIG_PATH_INFO'
6978 * Contains any client-provided pathname information trailing the actual script filename but preceding
6979 * the query string, if available. For instance, if the current script was accessed via the URL
6980 * http://www.example.com/jx9/path_info.jx9/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain
6981 * /some/stuff.
6982 */
6983 pValue = &sUri.sPath;
6984 jx9_vm_config(pVm,
6985 JX9_VM_CONFIG_SERVER_ATTR,
6986 "PATH_INFO",
6987 pValue->zString,
6988 pValue->nByte
6989 );
6990 jx9_vm_config(pVm,
6991 JX9_VM_CONFIG_SERVER_ATTR,
6992 "ORIG_PATH_INFO",
6993 pValue->zString,
6994 pValue->nByte
6995 );
6996 /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */
6997 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept", sizeof("Accept")-1);
6998 if( pValue ){
6999 jx9_vm_config(pVm,
7000 JX9_VM_CONFIG_SERVER_ATTR,
7001 "HTTP_ACCEPT",
7002 pValue->zString,
7003 pValue->nByte
7004 );
7005 }
7006 /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */
7007 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Charset", sizeof("Accept-Charset")-1);
7008 if( pValue ){
7009 jx9_vm_config(pVm,
7010 JX9_VM_CONFIG_SERVER_ATTR,
7011 "HTTP_ACCEPT_CHARSET",
7012 pValue->zString,
7013 pValue->nByte
7014 );
7015 }
7016 /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */
7017 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Encoding", sizeof("Accept-Encoding")-1);
7018 if( pValue ){
7019 jx9_vm_config(pVm,
7020 JX9_VM_CONFIG_SERVER_ATTR,
7021 "HTTP_ACCEPT_ENCODING",
7022 pValue->zString,
7023 pValue->nByte
7024 );
7025 }
7026 /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */
7027 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Language", sizeof("Accept-Language")-1);
7028 if( pValue ){
7029 jx9_vm_config(pVm,
7030 JX9_VM_CONFIG_SERVER_ATTR,
7031 "HTTP_ACCEPT_LANGUAGE",
7032 pValue->zString,
7033 pValue->nByte
7034 );
7035 }
7036 /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */
7037 pValue = VmHttpExtractHeaderValue(&sHeader, "Connection", sizeof("Connection")-1);
7038 if( pValue ){
7039 jx9_vm_config(pVm,
7040 JX9_VM_CONFIG_SERVER_ATTR,
7041 "HTTP_CONNECTION",
7042 pValue->zString,
7043 pValue->nByte
7044 );
7045 }
7046 /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */
7047 pValue = VmHttpExtractHeaderValue(&sHeader, "Host", sizeof("Host")-1);
7048 if( pValue ){
7049 jx9_vm_config(pVm,
7050 JX9_VM_CONFIG_SERVER_ATTR,
7051 "HTTP_HOST",
7052 pValue->zString,
7053 pValue->nByte
7054 );
7055 }
7056 /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */
7057 pValue = VmHttpExtractHeaderValue(&sHeader, "Referer", sizeof("Referer")-1);
7058 if( pValue ){
7059 jx9_vm_config(pVm,
7060 JX9_VM_CONFIG_SERVER_ATTR,
7061 "HTTP_REFERER",
7062 pValue->zString,
7063 pValue->nByte
7064 );
7065 }
7066 /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */
7067 pValue = VmHttpExtractHeaderValue(&sHeader, "User-Agent", sizeof("User-Agent")-1);
7068 if( pValue ){
7069 jx9_vm_config(pVm,
7070 JX9_VM_CONFIG_SERVER_ATTR,
7071 "HTTP_USER_AGENT",
7072 pValue->zString,
7073 pValue->nByte
7074 );
7075 }
7076 /* 'JX9_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization'
7077 * header sent by the client (which you should then use to make the appropriate validation).
7078 */
7079 pValue = VmHttpExtractHeaderValue(&sHeader, "Authorization", sizeof("Authorization")-1);
7080 if( pValue ){
7081 jx9_vm_config(pVm,
7082 JX9_VM_CONFIG_SERVER_ATTR,
7083 "JX9_AUTH_DIGEST",
7084 pValue->zString,
7085 pValue->nByte
7086 );
7087 jx9_vm_config(pVm,
7088 JX9_VM_CONFIG_SERVER_ATTR,
7089 "JX9_AUTH",
7090 pValue->zString,
7091 pValue->nByte
7092 );
7093 }
7094 /* Install all clients HTTP headers in the $_HEADER superglobal */
7095 pHeaderArray = VmExtractSuper(&(*pVm), "_HEADER", sizeof("_HEADER")-1);
7096 /* Iterate throw the available MIME headers*/
7097 SySetResetCursor(&sHeader);
7098 pHeader = 0; /* stupid cc warning */
7099 while( SXRET_OK == SySetGetNextEntry(&sHeader, (void **)&pHeader) ){
7100 pName = &pHeader->sName;
7101 pValue = &pHeader->sValue;
7102 if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){
7103 /* Insert the MIME header and it's associated value */
7104 VmHashmapInsert((jx9_hashmap *)pHeaderArray->x.pOther,
7105 pName->zString, (int)pName->nByte,
7106 pValue->zString, (int)pValue->nByte
7107 );
7108 }
7109 if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString, "Cookie", sizeof("Cookie")-1) == 0
7110 && pValue->nByte > 0){
7111 /* Process the name=value pair and insert them in the $_COOKIE superglobal array */
7112 VmHttpPorcessCookie(&(*pVm), &sWorker, pValue->zString, pValue->nByte);
7113 }
7114 }
7115 if( iMethod == HTTP_METHOD_POST ){
7116 /* Extract raw POST data */
7117 pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Type", sizeof("Content-Type") - 1);
7118 if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 &&
7119 SyMemcmp("application/x-www-form-urlencoded", pValue->zString, pValue->nByte) == 0 ){
7120 /* Extract POST data length */
7121 pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Length", sizeof("Content-Length") - 1);
7122 if( pValue ){
7123 sxi32 iLen = 0; /* POST data length */
7124 SyStrToInt32(pValue->zString, pValue->nByte, (void *)&iLen, 0);
7125 if( iLen > 0 ){
7126 /* Remove leading and trailing white spaces */
7127 SyStringFullTrim(&sRequest);
7128 if( (int)sRequest.nByte > iLen ){
7129 sRequest.nByte = (sxu32)iLen;
7130 }
7131 /* Decode POST data now */
7132 VmHttpSplitEncodedQuery(&(*pVm), &sRequest, &sWorker, TRUE);
7133 }
7134 }
7135 }
7136 }
7137 /* All done, clean-up the mess left behind */
7138 SySetRelease(&sHeader);
7139 SyBlobRelease(&sWorker);
7140 return SXRET_OK;
7141 }