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

11050 lines
348 KiB

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