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.

1999 lines
54 KiB

23 years ago
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2004 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.0 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_0.txt. |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Wez Furlong <wez@thebrainroom.com> |
  16. +----------------------------------------------------------------------+
  17. */
  18. /* $Id$ */
  19. /* Implementation Notes:
  20. *
  21. * PHP stores scripting engine state in thread-local storage. That means
  22. * that we need to create a dedicated thread per-engine so that a host can
  23. * use more than one engine object per thread.
  24. *
  25. * There are some interesting synchronization issues: Anything to do with
  26. * running script in the PHP/Zend engine must take place on the engine
  27. * thread. Likewise, calling back to the host must take place on the base
  28. * thread - the thread that set the script site.
  29. * */
  30. #define _WIN32_DCOM
  31. #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
  32. #include "php.h"
  33. extern "C" {
  34. #include "php_main.h"
  35. #include "SAPI.h"
  36. #include "zend.h"
  37. #include "zend_execute.h"
  38. #include "zend_compile.h"
  39. #include "php_globals.h"
  40. #include "php_variables.h"
  41. #include "php_ini.h"
  42. #include "php5activescript.h"
  43. #include "ext/com_dotnet/php_com_dotnet.h"
  44. #include "ext/com_dotnet/php_com_dotnet_internal.h"
  45. }
  46. #include "php_ticks.h"
  47. #include "php5as_scriptengine.h"
  48. #include "php5as_classfactory.h"
  49. #include <objbase.h>
  50. #undef php_win_err
  51. #define ACTIVEPHP_THREADING_MODE COINIT_MULTITHREADED
  52. /* {{{ trace */
  53. static inline void trace(char *fmt, ...)
  54. {
  55. va_list ap;
  56. char buf[4096];
  57. sprintf(buf, "T=%08x ", tsrm_thread_id());
  58. OutputDebugString(buf);
  59. va_start(ap, fmt);
  60. vsnprintf(buf, sizeof(buf), fmt, ap);
  61. OutputDebugString(buf);
  62. va_end(ap);
  63. }
  64. /* }}} */
  65. /* {{{ scriptstate_to_string */
  66. static const char *scriptstate_to_string(SCRIPTSTATE ss)
  67. {
  68. switch(ss) {
  69. case SCRIPTSTATE_UNINITIALIZED: return "SCRIPTSTATE_UNINITIALIZED";
  70. case SCRIPTSTATE_INITIALIZED: return "SCRIPTSTATE_INITIALIZED";
  71. case SCRIPTSTATE_STARTED: return "SCRIPTSTATE_STARTED";
  72. case SCRIPTSTATE_CONNECTED: return "SCRIPTSTATE_CONNECTED";
  73. case SCRIPTSTATE_DISCONNECTED: return "SCRIPTSTATE_DISCONNECTED";
  74. case SCRIPTSTATE_CLOSED: return "SCRIPTSTATE_CLOSED";
  75. default:
  76. return "unknown";
  77. }
  78. }
  79. /* }}} */
  80. /* {{{ TWideString */
  81. /* This class helps manipulate strings from OLE.
  82. * It does not use emalloc, so it is better suited for passing pointers
  83. * between threads. */
  84. class TWideString {
  85. public:
  86. LPOLESTR m_ole;
  87. char *m_ansi;
  88. int m_ansi_strlen;
  89. TWideString(LPOLESTR olestr) {
  90. m_ole = olestr;
  91. m_ansi = NULL;
  92. }
  93. TWideString(LPCOLESTR olestr) {
  94. m_ole = (LPOLESTR)olestr;
  95. m_ansi = NULL;
  96. }
  97. ~TWideString() {
  98. if (m_ansi) {
  99. CoTaskMemFree(m_ansi);
  100. }
  101. m_ansi = NULL;
  102. }
  103. char *safe_ansi_string() {
  104. char *ret = ansi_string();
  105. if (ret == NULL)
  106. return "<NULL>";
  107. return ret;
  108. }
  109. int ansi_len(void) {
  110. /* force conversion if it has not already occurred */
  111. if (m_ansi == NULL)
  112. ansi_string();
  113. return m_ansi_strlen;
  114. }
  115. static BSTR bstr_from_ansi(char *ansi) {
  116. OLECHAR *ole = NULL;
  117. BSTR bstr = NULL;
  118. int req = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0);
  119. if (req) {
  120. ole = (OLECHAR*)CoTaskMemAlloc((req + 1) * sizeof(OLECHAR));
  121. if (ole) {
  122. req = MultiByteToWideChar(CP_ACP, 0, ansi, -1, ole, req);
  123. req--;
  124. ole[req] = 0;
  125. bstr = SysAllocString(ole);
  126. CoTaskMemFree(ole);
  127. }
  128. }
  129. return bstr;
  130. }
  131. char *ansi_string(void)
  132. {
  133. if (m_ansi)
  134. return m_ansi;
  135. if (m_ole == NULL)
  136. return NULL;
  137. int bufrequired = WideCharToMultiByte(CP_ACP, 0, m_ole, -1, NULL, 0, NULL, NULL);
  138. if (bufrequired) {
  139. m_ansi = (char*)CoTaskMemAlloc(bufrequired + 1);
  140. if (m_ansi) {
  141. m_ansi_strlen = WideCharToMultiByte(CP_ACP, 0, m_ole, -1, m_ansi, bufrequired + 1, NULL, NULL);
  142. if (m_ansi_strlen) {
  143. m_ansi_strlen--;
  144. m_ansi[m_ansi_strlen] = 0;
  145. } else {
  146. trace("conversion failed with return code %08x\n", GetLastError());
  147. }
  148. }
  149. }
  150. return m_ansi;
  151. }
  152. };
  153. /* }}} */
  154. /* {{{ code fragment structures */
  155. enum fragtype {
  156. FRAG_MAIN,
  157. FRAG_SCRIPTLET,
  158. FRAG_PROCEDURE
  159. };
  160. typedef struct {
  161. enum fragtype fragtype;
  162. zend_op_array *opcodes;
  163. char *code;
  164. int persistent; /* should be retained for Clone */
  165. int executed; /* for "main" */
  166. char *functionname;
  167. unsigned int codelen;
  168. unsigned int starting_line;
  169. TPHPScriptingEngine *engine;
  170. void *ptr;
  171. } code_frag;
  172. #define FRAG_CREATE_FUNC (char*)-1
  173. static code_frag *compile_code_fragment(
  174. enum fragtype fragtype,
  175. char *functionname,
  176. LPCOLESTR code,
  177. ULONG starting_line,
  178. EXCEPINFO *excepinfo,
  179. TPHPScriptingEngine *engine
  180. TSRMLS_DC);
  181. static int execute_code_fragment(code_frag *frag,
  182. VARIANT *varResult,
  183. EXCEPINFO *excepinfo
  184. TSRMLS_DC);
  185. static void free_code_fragment(code_frag *frag TSRMLS_DC);
  186. static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC);
  187. /* }}} */
  188. /* Magic for handling threading correctly */
  189. static inline HRESULT SEND_THREAD_MESSAGE(TPHPScriptingEngine *engine, LONG msg, WPARAM wparam, LPARAM lparam TSRMLS_DC)
  190. {
  191. if (engine->m_enginethread == 0)
  192. return E_UNEXPECTED;
  193. if (tsrm_thread_id() == (engine)->m_enginethread)
  194. return (engine)->engine_thread_handler((msg), (wparam), (lparam), NULL TSRMLS_CC);
  195. return (engine)->SendThreadMessage((msg), (wparam), (lparam));
  196. }
  197. /* These functions do some magic so that interfaces can be
  198. * used across threads without worrying about marshalling
  199. * or not marshalling, as appropriate.
  200. * Win95 without DCOM 1.1, and NT SP 2 or lower do not have
  201. * the GIT; so we emulate the GIT using other means.
  202. * If you trace problems back to this code, installing the relevant
  203. * SP should solve them.
  204. * */
  205. static inline HRESULT GIT_get(DWORD cookie, REFIID riid, void **obj)
  206. {
  207. IGlobalInterfaceTable *git;
  208. HRESULT ret;
  209. if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,
  210. CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,
  211. (void**)&git))) {
  212. ret = git->GetInterfaceFromGlobal(cookie, riid, obj);
  213. git->Release();
  214. return ret;
  215. }
  216. return CoGetInterfaceAndReleaseStream((LPSTREAM)cookie, riid, obj);
  217. }
  218. static inline HRESULT GIT_put(IUnknown *unk, REFIID riid, DWORD *cookie)
  219. {
  220. IGlobalInterfaceTable *git;
  221. HRESULT ret;
  222. if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,
  223. CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,
  224. (void**)&git))) {
  225. ret = git->RegisterInterfaceInGlobal(unk, riid, cookie);
  226. git->Release();
  227. return ret;
  228. }
  229. return CoMarshalInterThreadInterfaceInStream(riid, unk, (LPSTREAM*)cookie);
  230. }
  231. static inline HRESULT GIT_revoke(DWORD cookie, IUnknown *unk)
  232. {
  233. IGlobalInterfaceTable *git;
  234. HRESULT ret;
  235. if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,
  236. CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,
  237. (void**)&git))) {
  238. ret = git->RevokeInterfaceFromGlobal(cookie);
  239. git->Release();
  240. }
  241. /* Kill remote clients */
  242. return CoDisconnectObject(unk, 0);
  243. }
  244. /* {{{ A generic stupid IDispatch implementation */
  245. class IDispatchImpl:
  246. public IDispatch
  247. {
  248. protected:
  249. volatile LONG m_refcount;
  250. public:
  251. /* IUnknown */
  252. STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject) {
  253. *ppvObject = NULL;
  254. if (IsEqualGUID(IID_IDispatch, iid)) {
  255. *ppvObject = (IDispatch*)this;
  256. } else if (IsEqualGUID(IID_IUnknown, iid)) {
  257. *ppvObject = this;
  258. }
  259. if (*ppvObject) {
  260. AddRef();
  261. return S_OK;
  262. }
  263. return E_NOINTERFACE;
  264. }
  265. STDMETHODIMP_(DWORD) AddRef(void) {
  266. return InterlockedIncrement(const_cast<long*> (&m_refcount));
  267. }
  268. STDMETHODIMP_(DWORD) Release(void) {
  269. DWORD ret = InterlockedDecrement(const_cast<long*> (&m_refcount));
  270. trace("%08x: IDispatchImpl: release ref count is now %d\n", this, ret);
  271. if (ret == 0)
  272. delete this;
  273. return ret;
  274. }
  275. /* IDispatch */
  276. STDMETHODIMP GetTypeInfoCount(unsigned int * pctinfo) {
  277. *pctinfo = 0;
  278. trace("%08x: IDispatchImpl: GetTypeInfoCount\n", this);
  279. return S_OK;
  280. }
  281. STDMETHODIMP GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
  282. trace("%08x: IDispatchImpl: GetTypeInfo\n", this);
  283. return DISP_E_BADINDEX;
  284. }
  285. STDMETHODIMP GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID *rgDispId)
  286. {
  287. unsigned int i;
  288. trace("%08x: IDispatchImpl: GetIDsOfNames: \n", this);
  289. for (i = 0; i < cNames; i++) {
  290. TWideString name(rgszNames[i]);
  291. trace(" %s\n", name.ansi_string());
  292. }
  293. trace("----\n");
  294. return DISP_E_UNKNOWNNAME;
  295. }
  296. STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
  297. DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
  298. unsigned int FAR* puArgErr)
  299. {
  300. trace("%08x: IDispatchImpl: Invoke dispid %08x\n", this, dispIdMember);
  301. return S_OK;
  302. }
  303. IDispatchImpl() {
  304. m_refcount = 1;
  305. }
  306. virtual ~IDispatchImpl() {
  307. }
  308. };
  309. /* }}} */
  310. /* {{{ This object represents the PHP engine to the scripting host.
  311. * Although the docs say it's implementation is optional, I found that
  312. * the Windows Script host would crash if we did not provide it. */
  313. class ScriptDispatch:
  314. public IDispatchImpl
  315. {
  316. public:
  317. ScriptDispatch() {
  318. m_refcount = 1;
  319. }
  320. };
  321. /* }}} */
  322. /* {{{ This object is used in conjunction with IActiveScriptParseProcedure to
  323. * allow scriptlets to be bound to events. IE uses this for declaring
  324. * event handlers such as onclick="...".
  325. * The compiled code is stored in this object; IE will call
  326. * IDispatch::Invoke when the element is clicked.
  327. * */
  328. class ScriptProcedureDispatch:
  329. public IDispatchImpl
  330. {
  331. public:
  332. code_frag *m_frag;
  333. DWORD m_procflags;
  334. TPHPScriptingEngine *m_engine;
  335. DWORD m_gitcookie;
  336. STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
  337. DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
  338. unsigned int FAR* puArgErr)
  339. {
  340. TSRMLS_FETCH();
  341. if (m_frag) {
  342. trace("%08x: Procedure Dispatch: Invoke dispid %08x\n", this, dispIdMember);
  343. SEND_THREAD_MESSAGE(m_engine, PHPSE_EXEC_PROC, 0, (LPARAM)this TSRMLS_CC);
  344. }
  345. return S_OK;
  346. }
  347. ScriptProcedureDispatch() {
  348. m_refcount = 1;
  349. GIT_put((IDispatch*)this, IID_IDispatch, &m_gitcookie);
  350. }
  351. };
  352. /* }}} */
  353. /* {{{ code fragment management */
  354. static code_frag *compile_code_fragment(
  355. enum fragtype fragtype,
  356. char *functionname,
  357. LPCOLESTR code,
  358. ULONG starting_line,
  359. EXCEPINFO *excepinfo,
  360. TPHPScriptingEngine *engine
  361. TSRMLS_DC)
  362. {
  363. zval pv;
  364. int code_offs = 0;
  365. char namebuf[256];
  366. code_frag *frag = (code_frag*)CoTaskMemAlloc(sizeof(code_frag));
  367. memset(frag, 0, sizeof(code_frag));
  368. frag->engine = engine;
  369. /* handle the function name */
  370. if (functionname) {
  371. int namelen;
  372. if (functionname == FRAG_CREATE_FUNC) {
  373. ULONG n = ++engine->m_lambda_count;
  374. sprintf(namebuf, "__frag_%08x_%u", engine, n);
  375. functionname = namebuf;
  376. }
  377. namelen = strlen(functionname);
  378. code_offs = namelen + sizeof("function (){");
  379. frag->functionname = (char*)CoTaskMemAlloc((namelen + 1) * sizeof(char));
  380. memcpy(frag->functionname, functionname, namelen+1);
  381. }
  382. frag->functionname = functionname;
  383. trace("%08x: COMPILED FRAG\n", frag);
  384. frag->codelen = WideCharToMultiByte(CP_ACP, 0, code, -1, NULL, 0, NULL, NULL);
  385. frag->code = (char*)CoTaskMemAlloc(sizeof(char) * (frag->codelen + code_offs + 1));
  386. if (functionname) {
  387. sprintf(frag->code, "function %s(){ ", functionname);
  388. }
  389. frag->codelen = WideCharToMultiByte(CP_ACP, 0, code, -1, frag->code + code_offs, frag->codelen, NULL, NULL) - 1;
  390. if (functionname) {
  391. frag->codelen += code_offs + 1;
  392. frag->code[frag->codelen-1] = '}';
  393. frag->code[frag->codelen] = 0;
  394. }
  395. trace("code to compile is:\ncode_offs=%d func=%s\n%s\n", code_offs, functionname, frag->code);
  396. frag->fragtype = fragtype;
  397. frag->starting_line = starting_line;
  398. pv.type = IS_STRING;
  399. pv.value.str.val = frag->code;
  400. pv.value.str.len = frag->codelen;
  401. frag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC);
  402. if (frag->opcodes == NULL) {
  403. free_code_fragment(frag TSRMLS_CC);
  404. if (excepinfo) {
  405. memset(excepinfo, 0, sizeof(EXCEPINFO));
  406. excepinfo->wCode = 1000;
  407. excepinfo->bstrSource = TWideString::bstr_from_ansi("fragment");
  408. excepinfo->bstrDescription = TWideString::bstr_from_ansi("Problem while parsing/compiling");
  409. }
  410. return NULL;
  411. }
  412. return frag;
  413. }
  414. static void free_code_fragment(code_frag *frag TSRMLS_DC)
  415. {
  416. switch(frag->fragtype) {
  417. case FRAG_PROCEDURE:
  418. if (frag->ptr) {
  419. ScriptProcedureDispatch *disp = (ScriptProcedureDispatch*)frag->ptr;
  420. disp->Release();
  421. GIT_revoke(disp->m_gitcookie, (IDispatch*)disp);
  422. frag->ptr = NULL;
  423. }
  424. break;
  425. }
  426. if (frag->opcodes)
  427. destroy_op_array(frag->opcodes TSRMLS_CC);
  428. if (frag->functionname)
  429. CoTaskMemFree(frag->functionname);
  430. CoTaskMemFree(frag->code);
  431. CoTaskMemFree(frag);
  432. }
  433. static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC)
  434. {
  435. zval pv;
  436. code_frag *newfrag = (code_frag*)CoTaskMemAlloc(sizeof(code_frag));
  437. memset(newfrag, 0, sizeof(code_frag));
  438. newfrag->engine = engine;
  439. trace("%08x: CLONED FRAG\n", newfrag);
  440. newfrag->persistent = frag->persistent;
  441. newfrag->codelen = frag->codelen;
  442. newfrag->code = (char*)CoTaskMemAlloc(sizeof(char) * frag->codelen + 1);
  443. memcpy(newfrag->code, frag->code, frag->codelen + 1);
  444. if (frag->functionname) {
  445. int namelen = strlen(frag->functionname);
  446. newfrag->functionname = (char*)CoTaskMemAlloc(sizeof(char) * (namelen + 1));
  447. memcpy(newfrag->functionname, frag->functionname, namelen+1);
  448. } else {
  449. newfrag->functionname = NULL;
  450. }
  451. newfrag->fragtype = frag->fragtype;
  452. newfrag->starting_line = frag->starting_line;
  453. pv.type = IS_STRING;
  454. pv.value.str.val = newfrag->code;
  455. pv.value.str.len = newfrag->codelen;
  456. newfrag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC);
  457. if (newfrag->opcodes == NULL) {
  458. free_code_fragment(newfrag TSRMLS_CC);
  459. /*
  460. if (excepinfo) {
  461. memset(excepinfo, 0, sizeof(EXCEPINFO));
  462. excepinfo->wCode = 1000;
  463. excepinfo->bstrSource = TWideString::bstr_from_ansi("fragment");
  464. excepinfo->bstrDescription = TWideString::bstr_from_ansi("Problem while parsing/compiling");
  465. }
  466. */
  467. return NULL;
  468. }
  469. return newfrag;
  470. }
  471. static int execute_code_fragment(code_frag *frag,
  472. VARIANT *varResult,
  473. EXCEPINFO *excepinfo
  474. TSRMLS_DC)
  475. {
  476. zval *retval_ptr = NULL;
  477. jmp_buf *orig_jmpbuf;
  478. jmp_buf err_trap;
  479. if (frag->fragtype == FRAG_MAIN && frag->executed)
  480. return 1;
  481. orig_jmpbuf = frag->engine->m_err_trap;
  482. frag->engine->m_err_trap = &err_trap;
  483. if (setjmp(err_trap) == 0) {
  484. trace("*** Executing code in thread %08x\n", tsrm_thread_id());
  485. if (frag->functionname) {
  486. zval fname;
  487. fname.type = IS_STRING;
  488. fname.value.str.val = frag->functionname;
  489. fname.value.str.len = strlen(frag->functionname);
  490. call_user_function_ex(CG(function_table), NULL, &fname, &retval_ptr, 0, NULL, 1, NULL TSRMLS_CC);
  491. } else {
  492. zend_op_array *active_op_array = EG(active_op_array);
  493. zend_function_state *function_state_ptr = EG(function_state_ptr);
  494. zval **return_value_ptr_ptr = EG(return_value_ptr_ptr);
  495. zend_op **opline_ptr = EG(opline_ptr);
  496. EG(return_value_ptr_ptr) = &retval_ptr;
  497. EG(active_op_array) = frag->opcodes;
  498. EG(no_extensions) = 1;
  499. zend_execute(frag->opcodes TSRMLS_CC);
  500. EG(no_extensions) = 0;
  501. EG(opline_ptr) = opline_ptr;
  502. EG(active_op_array) = active_op_array;
  503. EG(function_state_ptr) = function_state_ptr;
  504. EG(return_value_ptr_ptr) = return_value_ptr_ptr;
  505. }
  506. } else {
  507. trace("*** --> caught error while executing\n");
  508. if (frag->engine->m_in_main)
  509. frag->engine->m_stop_main = 1;
  510. }
  511. frag->engine->m_err_trap = orig_jmpbuf;
  512. if (frag->fragtype == FRAG_MAIN)
  513. frag->executed = 1;
  514. if (varResult)
  515. VariantInit(varResult);
  516. if (retval_ptr) {
  517. if (varResult)
  518. php_com_variant_from_zval(varResult, retval_ptr, CP_ACP TSRMLS_CC);
  519. zval_ptr_dtor(&retval_ptr);
  520. }
  521. return 1;
  522. }
  523. static void frag_dtor(void *pDest)
  524. {
  525. TSRMLS_FETCH();
  526. code_frag *frag = *(code_frag**)pDest;
  527. free_code_fragment(frag TSRMLS_CC);
  528. }
  529. /* }}} */
  530. /* glue for getting back into the OO land */
  531. static DWORD WINAPI begin_engine_thread(LPVOID param)
  532. {
  533. TPHPScriptingEngine *engine = (TPHPScriptingEngine*)param;
  534. engine->engine_thread_func();
  535. trace("engine thread has really gone away!\n");
  536. return 0;
  537. }
  538. TPHPScriptingEngine::TPHPScriptingEngine()
  539. {
  540. m_scriptstate = SCRIPTSTATE_UNINITIALIZED;
  541. m_pass = NULL;
  542. m_in_main = 0;
  543. m_stop_main = 0;
  544. m_err_trap = NULL;
  545. m_lambda_count = 0;
  546. m_pass_eng = NULL;
  547. m_refcount = 1;
  548. m_basethread = tsrm_thread_id();
  549. m_mutex = tsrm_mutex_alloc();
  550. m_sync_thread_msg = CreateEvent(NULL, TRUE, FALSE, NULL);
  551. TPHPClassFactory::AddToObjectCount();
  552. m_engine_thread_handle = CreateThread(NULL, 0, begin_engine_thread, this, 0, &m_enginethread);
  553. }
  554. void activescript_run_ticks(int count)
  555. {
  556. MSG msg;
  557. TSRMLS_FETCH();
  558. TPHPScriptingEngine *engine;
  559. trace("ticking %d\n", count);
  560. engine = (TPHPScriptingEngine*)SG(server_context);
  561. /* PostThreadMessage(engine->m_enginethread, PHPSE_DUMMY_TICK, 0, 0); */
  562. while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
  563. if (msg.hwnd) {
  564. PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
  565. TranslateMessage(&msg);
  566. DispatchMessage(&msg);
  567. } else {
  568. break;
  569. }
  570. }
  571. }
  572. /* Synchronize with the engine thread */
  573. HRESULT TPHPScriptingEngine::SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam)
  574. {
  575. HRESULT ret;
  576. if (m_enginethread == 0)
  577. return E_UNEXPECTED;
  578. trace("I'm waiting for a mutex in SendThreadMessage\n this=%08x ethread=%08x msg=%08x\n",
  579. this, m_enginethread, msg);
  580. tsrm_mutex_lock(m_mutex);
  581. ResetEvent(m_sync_thread_msg);
  582. /* If we call PostThreadMessage before the thread has created the queue, the message
  583. * posting fails. MSDN docs recommend the following course of action */
  584. while (!PostThreadMessage(m_enginethread, msg, wparam, lparam)) {
  585. Sleep(50);
  586. if (m_enginethread == 0) {
  587. tsrm_mutex_unlock(m_mutex);
  588. trace("breaking out of dodgy busy wait\n");
  589. return E_UNEXPECTED;
  590. }
  591. }
  592. /* Wait for the event object to be signalled.
  593. * This is a nice "blocking without blocking" wait; window messages are dispatched
  594. * and everything works out quite nicely */
  595. while(1) {
  596. DWORD result = MsgWaitForMultipleObjects(1, &m_sync_thread_msg, FALSE, 4000, QS_ALLINPUT);
  597. if (result == WAIT_OBJECT_0 + 1) {
  598. /* Dispatch some messages */
  599. MSG msg;
  600. while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  601. //trace("dispatching message while waiting\n");
  602. TranslateMessage(&msg);
  603. DispatchMessage(&msg);
  604. }
  605. } else if (result == WAIT_TIMEOUT) {
  606. trace("timeout while waiting for thread reply\n");
  607. } else {
  608. /* the event was signalled */
  609. break;
  610. }
  611. }
  612. ret = m_sync_thread_ret;
  613. ResetEvent(m_sync_thread_msg);
  614. tsrm_mutex_unlock(m_mutex);
  615. return ret;
  616. }
  617. TPHPScriptingEngine::~TPHPScriptingEngine()
  618. {
  619. trace("\n\n *** Engine Destructor Called\n\n");
  620. if (m_scriptstate != SCRIPTSTATE_UNINITIALIZED && m_scriptstate != SCRIPTSTATE_CLOSED && m_enginethread) {
  621. Close();
  622. }
  623. PostThreadMessage(m_enginethread, WM_QUIT, 0, 0);
  624. WaitForSingleObject(m_engine_thread_handle, INFINITE);
  625. CloseHandle(m_engine_thread_handle);
  626. TPHPClassFactory::RemoveFromObjectCount();
  627. tsrm_mutex_free(m_mutex);
  628. }
  629. /* Set some executor globals and execute a zend_op_array.
  630. * The declaration looks wierd because this can be invoked from
  631. * zend_hash_apply_with_argument */
  632. static int execute_main(void *pDest, void *arg TSRMLS_DC)
  633. {
  634. code_frag *frag = *(code_frag**)pDest;
  635. if (frag->fragtype == FRAG_MAIN && !(frag->engine->m_in_main && frag->engine->m_stop_main))
  636. execute_code_fragment(frag, NULL, NULL TSRMLS_CC);
  637. return ZEND_HASH_APPLY_KEEP;
  638. }
  639. static int clone_frags(void *pDest, void *arg TSRMLS_DC)
  640. {
  641. code_frag *frag, *src = *(code_frag**)pDest;
  642. TPHPScriptingEngine *engine = (TPHPScriptingEngine*)arg;
  643. if (src->persistent) {
  644. frag = clone_code_fragment(src, engine TSRMLS_CC);
  645. if (frag)
  646. zend_hash_next_index_insert(&engine->m_frags, &frag, sizeof(code_frag*), NULL);
  647. else
  648. trace("WARNING: clone failed!\n");
  649. }
  650. return ZEND_HASH_APPLY_KEEP;
  651. }
  652. HRESULT TPHPScriptingEngine::engine_thread_handler(LONG msg, WPARAM wparam, LPARAM lParam, int *handled TSRMLS_DC)
  653. {
  654. HRESULT ret = S_OK;
  655. trace("engine_thread_handler: running in thread %08x, should be %08x msg=%08x this=%08x\n",
  656. tsrm_thread_id(), m_enginethread, msg, this);
  657. if (handled)
  658. *handled = 1;
  659. if (m_enginethread == 0)
  660. return E_UNEXPECTED;
  661. switch(msg) {
  662. case PHPSE_ADD_TYPELIB:
  663. {
  664. struct php_active_script_add_tlb_info *info = (struct php_active_script_add_tlb_info*)lParam;
  665. ITypeLib *TypeLib;
  666. if (SUCCEEDED(LoadRegTypeLib(*info->rguidTypeLib, (USHORT)info->dwMajor,
  667. (USHORT)info->dwMinor, LANG_NEUTRAL, &TypeLib))) {
  668. php_com_import_typelib(TypeLib, CONST_CS, CP_ACP TSRMLS_CC);
  669. TypeLib->Release();
  670. }
  671. }
  672. break;
  673. case PHPSE_STATE_CHANGE:
  674. {
  675. /* handle the state change here */
  676. SCRIPTSTATE ss = (SCRIPTSTATE)lParam;
  677. int start_running = 0;
  678. trace("%08x: DoSetScriptState(current=%s, new=%s)\n",
  679. this,
  680. scriptstate_to_string(m_scriptstate),
  681. scriptstate_to_string(ss));
  682. if (m_scriptstate == SCRIPTSTATE_INITIALIZED && (ss == SCRIPTSTATE_STARTED || ss == SCRIPTSTATE_CONNECTED))
  683. start_running = 1;
  684. m_scriptstate = ss;
  685. /* inform host/site of the change */
  686. if (m_pass_eng)
  687. m_pass_eng->OnStateChange(m_scriptstate);
  688. if (start_running) {
  689. /* run "main()", as described in the docs */
  690. if (m_pass_eng)
  691. m_pass_eng->OnEnterScript();
  692. trace("%08x: apply execute main to m_frags\n", this);
  693. m_in_main = 1;
  694. m_stop_main = 0;
  695. zend_hash_apply_with_argument(&m_frags, execute_main, this TSRMLS_CC);
  696. m_in_main = 0;
  697. trace("%08x: --- done execute main\n", this);
  698. if (m_pass_eng)
  699. m_pass_eng->OnLeaveScript();
  700. /* docs are a bit ambiguous here, but it appears that we should
  701. * inform the host that the main script execution has completed,
  702. * and also what the return value is */
  703. VARIANT varRes;
  704. VariantInit(&varRes);
  705. if (m_pass_eng)
  706. m_pass_eng->OnScriptTerminate(&varRes, NULL);
  707. /*
  708. m_scriptstate = SCRIPTSTATE_INITIALIZED;
  709. if (m_pass_eng)
  710. m_pass_eng->OnStateChange(m_scriptstate);
  711. */
  712. }
  713. }
  714. break;
  715. case PHPSE_INIT_NEW:
  716. {
  717. /* Prepare PHP/ZE for use */
  718. trace("%08x: m_frags : INIT NEW\n", this);
  719. #if 0
  720. zend_hash_init(&m_frags, 0, NULL, frag_dtor, TRUE);
  721. SG(options) |= SAPI_OPTION_NO_CHDIR;
  722. SG(server_context) = this;
  723. /* override the default PHP error callback */
  724. zend_error_cb = activescript_error_handler;
  725. zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  726. zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  727. zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  728. zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  729. php_request_startup(TSRMLS_C);
  730. PG(during_request_startup) = 0;
  731. // trace("\n\n *** ticks func at %08x %08x ***\n\n\n", activescript_run_ticks, &activescript_run_ticks);
  732. // php_add_tick_function(activescript_run_ticks);
  733. #endif
  734. }
  735. break;
  736. case PHPSE_CLOSE:
  737. {
  738. /* Close things down */
  739. trace("%08x: m_frags : CLOSE/DESTROY\n", this);
  740. m_scriptstate = SCRIPTSTATE_CLOSED;
  741. if (m_pass_eng) {
  742. m_pass_eng->OnStateChange(m_scriptstate);
  743. trace("%08x: release site from this side\n", this);
  744. m_pass_eng->Release();
  745. m_pass_eng = NULL;
  746. }
  747. #if 0
  748. zend_hash_destroy(&m_frags);
  749. php_request_shutdown(NULL);
  750. #endif
  751. break;
  752. }
  753. break;
  754. case PHPSE_CLONE:
  755. {
  756. /* Clone the engine state. This is semantically equal to serializing all
  757. * the parsed code from the source and unserializing it in the dest (this).
  758. * IE doesn't appear to use it, but Windows Script Host does. I'd expect
  759. * ASP/ASP.NET to do so also.
  760. *
  761. * FIXME: Probably won't work with IActiveScriptParseProcedure scriplets
  762. * */
  763. TPHPScriptingEngine *src = (TPHPScriptingEngine*)lParam;
  764. trace("%08x: m_frags : CLONE\n", this);
  765. zend_hash_apply_with_argument(&src->m_frags, clone_frags, this TSRMLS_CC);
  766. }
  767. break;
  768. case PHPSE_ADD_SCRIPTLET:
  769. {
  770. /* Parse/compile a chunk of script that will act as an event handler.
  771. * If the host supports IActiveScriptParseProcedure, this code will
  772. * not be called.
  773. * The docs are (typically) vague: AFAICT, once the code has been
  774. * compiled, we are supposed to arrange for an IConnectionPoint
  775. * advisory connection to the item/subitem, once the script
  776. * moves into SCRIPTSTATE_CONNECTED.
  777. * That's a lot of work!
  778. *
  779. * FIXME: this is currently almost useless
  780. * */
  781. struct php_active_script_add_scriptlet_info *info = (struct php_active_script_add_scriptlet_info*)lParam;
  782. TWideString
  783. default_name(info->pstrDefaultName),
  784. code(info->pstrCode),
  785. item_name(info->pstrItemName),
  786. sub_item_name(info->pstrSubItemName),
  787. event_name(info->pstrEventName),
  788. delimiter(info->pstrDelimiter);
  789. /* lets invent a function name for the scriptlet */
  790. char sname[256];
  791. /* should check if the name is already used! */
  792. if (info->pstrDefaultName)
  793. strcpy(sname, default_name.ansi_string());
  794. else {
  795. sname[0] = 0;
  796. strcat(sname, "__");
  797. if (info->pstrItemName) {
  798. strcat(sname, item_name.ansi_string());
  799. strcat(sname, "_");
  800. }
  801. if (info->pstrSubItemName) {
  802. strcat(sname, sub_item_name.ansi_string());
  803. strcat(sname, "_");
  804. }
  805. if (info->pstrEventName)
  806. strcat(sname, event_name.ansi_string());
  807. }
  808. trace("%08x: AddScriptlet:\n state=%s\n name=%s\n code=%s\n item=%s\n subitem=%s\n event=%s\n delim=%s\n line=%d\n",
  809. this, scriptstate_to_string(m_scriptstate),
  810. default_name.safe_ansi_string(), code.safe_ansi_string(), item_name.safe_ansi_string(),
  811. sub_item_name.safe_ansi_string(), event_name.safe_ansi_string(), delimiter.safe_ansi_string(),
  812. info->ulStartingLineNumber);
  813. code_frag *frag = compile_code_fragment(
  814. FRAG_SCRIPTLET,
  815. sname,
  816. info->pstrCode,
  817. info->ulStartingLineNumber,
  818. info->pexcepinfo,
  819. this
  820. TSRMLS_CC);
  821. if (frag) {
  822. frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
  823. zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
  824. /*
  825. ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
  826. disp->AddRef();
  827. disp->m_frag = frag;
  828. disp->m_procflags = info->dwFlags;
  829. disp->m_engine = this;
  830. frag->ptr = disp;
  831. *info->ppdisp = disp;
  832. */
  833. ret = S_OK;
  834. } else {
  835. ret = DISP_E_EXCEPTION;
  836. }
  837. *info->pbstrName = TWideString::bstr_from_ansi(sname);
  838. trace("%08x: done with scriptlet %s\n", this, sname);
  839. }
  840. break;
  841. case PHPSE_GET_DISPATCH:
  842. {
  843. struct php_active_script_get_dispatch_info *info = (struct php_active_script_get_dispatch_info *)lParam;
  844. IDispatch *disp = NULL;
  845. if (info->pstrItemName != NULL) {
  846. zval **tmp;
  847. /* use this rather than php_OLECHAR_to_char because we want to avoid emalloc here */
  848. TWideString itemname(info->pstrItemName);
  849. /* Get that item from the global namespace.
  850. * If it is an object, export it as a dispatchable object.
  851. * */
  852. if (zend_hash_find(&EG(symbol_table), itemname.ansi_string(),
  853. itemname.ansi_len() + 1, (void**)&tmp) == SUCCESS) {
  854. if (Z_TYPE_PP(tmp) == IS_OBJECT) {
  855. /* FIXME: if this causes an allocation (emalloc) and we are
  856. * not in the engine thread, things could get ugly!!! */
  857. disp = php_com_wrapper_export(*tmp TSRMLS_CC);
  858. }
  859. }
  860. } else {
  861. /* This object represents PHP global namespace */
  862. disp = (IDispatch*) new ScriptDispatch;
  863. }
  864. if (disp) {
  865. ret = GIT_put(disp, IID_IDispatch, &info->dispatch);
  866. disp->Release();
  867. trace("GET_DISPATCH: we put it in the GIT\n");
  868. } else {
  869. ret = S_FALSE;
  870. trace("GET_DISPATCH: FAILED to put it in the GIT\n");
  871. }
  872. }
  873. break;
  874. case PHPSE_ADD_NAMED_ITEM:
  875. {
  876. /* The Host uses this to add objects to the global namespace.
  877. * Some objects are intended to have their child properties
  878. * globally visible, so we add those to the global namespace too.
  879. * */
  880. struct php_active_script_add_named_item_info *info = (struct php_active_script_add_named_item_info *)lParam;
  881. TWideString name(info->pstrName);
  882. IDispatch *disp = NULL;
  883. HRESULT res;
  884. trace("ADD_NAMED_ITEM\n");
  885. #if ACTIVEPHP_THREADING_MODE == COINIT_MULTITHREADED
  886. res = info->punk->QueryInterface(IID_IDispatch, (void**)&disp);
  887. #else
  888. res = GIT_get(info->marshal, IID_IDispatch, (void**)&disp);
  889. #endif
  890. if (SUCCEEDED(res) && disp) {
  891. add_to_global_namespace(disp, info->dwFlags, name.ansi_string() TSRMLS_CC);
  892. disp->Release();
  893. } else {
  894. trace("Ouch: failed to get IDispatch for %s from GIT '%s'\n", name.ansi_string(), php_win_err(res));
  895. }
  896. }
  897. break;
  898. case PHPSE_SET_SITE:
  899. {
  900. if (m_pass_eng) {
  901. m_pass_eng->Release();
  902. m_pass_eng = NULL;
  903. }
  904. if (lParam)
  905. GIT_get(lParam, IID_IActiveScriptSite, (void**)&m_pass_eng);
  906. trace("%08x: site (engine-side) is now %08x (base=%08x)\n", this, m_pass_eng, m_pass);
  907. }
  908. break;
  909. case PHPSE_EXEC_PROC:
  910. {
  911. ScriptProcedureDispatch *disp = (ScriptProcedureDispatch *)lParam;
  912. execute_code_fragment(disp->m_frag, NULL, NULL TSRMLS_CC);
  913. }
  914. break;
  915. case PHPSE_PARSE_PROC:
  916. {
  917. /* This is the IActiveScriptParseProcedure implementation.
  918. * IE uses this to request for an IDispatch that it will invoke in
  919. * response to some event, and tells us the code that it wants to
  920. * run.
  921. * We compile the code and pass it back a dispatch object.
  922. * The object will then serialize access to the engine thread and
  923. * execute the opcodes */
  924. struct php_active_script_parse_proc_info *info = (struct php_active_script_parse_proc_info*)lParam;
  925. TWideString
  926. formal_params(info->pstrFormalParams),
  927. procedure_name(info->pstrProcedureName),
  928. item_name(info->pstrItemName),
  929. delimiter(info->pstrDelimiter);
  930. trace("%08x: ParseProc:\n state=%s\nparams=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
  931. this, scriptstate_to_string(m_scriptstate),
  932. formal_params.ansi_string(), procedure_name.ansi_string(),
  933. item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
  934. info->ulStartingLineNumber);
  935. code_frag *frag = compile_code_fragment(
  936. FRAG_PROCEDURE,
  937. NULL,
  938. info->pstrCode,
  939. info->ulStartingLineNumber,
  940. NULL,
  941. this
  942. TSRMLS_CC);
  943. if (frag) {
  944. frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
  945. zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
  946. ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
  947. disp->m_frag = frag;
  948. disp->m_procflags = info->dwFlags;
  949. disp->m_engine = this;
  950. frag->ptr = disp;
  951. info->dispcookie = disp->m_gitcookie;
  952. } else {
  953. ret = DISP_E_EXCEPTION;
  954. }
  955. }
  956. break;
  957. case PHPSE_PARSE_SCRIPT:
  958. {
  959. struct php_active_script_parse_info *info = (struct php_active_script_parse_info*)lParam;
  960. int doexec;
  961. TWideString
  962. code(info->pstrCode),
  963. item_name(info->pstrItemName),
  964. delimiter(info->pstrDelimiter);
  965. trace("%08x: ParseScriptText:\n state=%s\ncode=%s\n item=%s\n delim=%s\n line=%d\n",
  966. this, scriptstate_to_string(m_scriptstate),
  967. code.safe_ansi_string(), item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
  968. info->ulStartingLineNumber);
  969. code_frag *frag = compile_code_fragment(
  970. FRAG_MAIN,
  971. info->dwFlags & SCRIPTTEXT_ISEXPRESSION ? FRAG_CREATE_FUNC : NULL,
  972. info->pstrCode,
  973. info->ulStartingLineNumber,
  974. info->pexcepinfo,
  975. this
  976. TSRMLS_CC);
  977. doexec = (info->dwFlags & SCRIPTTEXT_ISEXPRESSION) ||
  978. m_scriptstate == SCRIPTSTATE_STARTED ||
  979. m_scriptstate == SCRIPTSTATE_CONNECTED ||
  980. m_scriptstate == SCRIPTSTATE_DISCONNECTED;
  981. if (frag) {
  982. frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
  983. ret = S_OK;
  984. if (info->dwFlags & SCRIPTTEXT_ISEXPRESSION) {
  985. if (m_scriptstate == SCRIPTSTATE_INITIALIZED) {
  986. /* not allowed to execute code in this state */
  987. ret = E_UNEXPECTED;
  988. doexec = 0;
  989. }
  990. }
  991. if (doexec) {
  992. /* execute the code as an expression */
  993. if (!execute_code_fragment(frag, info->pvarResult, info->pexcepinfo TSRMLS_CC))
  994. ret = DISP_E_EXCEPTION;
  995. }
  996. zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
  997. } else {
  998. ret = DISP_E_EXCEPTION;
  999. }
  1000. if (info->pvarResult) {
  1001. VariantInit(info->pvarResult);
  1002. }
  1003. }
  1004. break;
  1005. default:
  1006. trace("XXXXX: unhandled message type %08x\n", msg);
  1007. if (handled)
  1008. *handled = 0;
  1009. }
  1010. return ret;
  1011. }
  1012. /* The PHP/Zend state actually lives in this thread */
  1013. void TPHPScriptingEngine::engine_thread_func(void)
  1014. {
  1015. TSRMLS_FETCH();
  1016. int handled;
  1017. int terminated = 0;
  1018. MSG msg;
  1019. trace("%08x: engine thread started up!\n", this);
  1020. CoInitializeEx(0, ACTIVEPHP_THREADING_MODE);
  1021. zend_first_try {
  1022. m_tsrm_hack = tsrm_ls;
  1023. SG(options) |= SAPI_OPTION_NO_CHDIR;
  1024. SG(headers_sent) = 1;
  1025. SG(request_info).no_headers = 1;
  1026. SG(server_context) = this;
  1027. /* override the default PHP error callback */
  1028. zend_error_cb = activescript_error_handler;
  1029. zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  1030. zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  1031. zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  1032. zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  1033. if (SUCCESS == php_request_startup(TSRMLS_C)) {
  1034. PG(during_request_startup) = 0;
  1035. if (FAILURE == zend_hash_init(&m_frags, 0, NULL, frag_dtor, 1)) {
  1036. trace("failed to init frags hash\n");
  1037. }
  1038. while(!terminated) {
  1039. DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, 4000, QS_ALLINPUT);
  1040. switch(result) {
  1041. case WAIT_OBJECT_0:
  1042. while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  1043. if (msg.message == WM_QUIT) {
  1044. terminated = 1;
  1045. } else if (msg.hwnd) {
  1046. TranslateMessage(&msg);
  1047. DispatchMessage(&msg);
  1048. } else {
  1049. handled = 1;
  1050. m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
  1051. if (handled)
  1052. SetEvent(m_sync_thread_msg);
  1053. }
  1054. }
  1055. break;
  1056. case WAIT_TIMEOUT:
  1057. trace("thread wait timed out\n");
  1058. break;
  1059. default:
  1060. trace("some strange value\n");
  1061. }
  1062. }
  1063. #if 0
  1064. while(GetMessage(&msg, NULL, 0, 0)) {
  1065. if (msg.message == WM_QUIT)
  1066. break;
  1067. handled = 1;
  1068. m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
  1069. if (handled)
  1070. SetEvent(m_sync_thread_msg);
  1071. }
  1072. #endif
  1073. trace("%08x: engine thread exiting!!!!!\n", this);
  1074. zend_hash_destroy(&m_frags);
  1075. trace("calling request shutdown\n");
  1076. php_request_shutdown(NULL);
  1077. } else {
  1078. trace("request startup failed\n");
  1079. }
  1080. } zend_catch {
  1081. trace("ouch: bailed out\n");
  1082. } zend_end_try();
  1083. m_enginethread = 0;
  1084. CoUninitialize();
  1085. }
  1086. /* Only call this in the context of the engine thread, or you'll be sorry.
  1087. *
  1088. * When SCRIPTITEM_GLOBALMEMBERS is set, we're only adding COM objects to the namespace.
  1089. * We could add *all* properties, but I don't like this idea; what if the value changes
  1090. * while the page is running? We'd be left with stale data.
  1091. *
  1092. * TODO: evaluate if it is appropriate to register as an auto_global
  1093. * */
  1094. static inline void make_auto_global(char *name, zval *val TSRMLS_DC)
  1095. {
  1096. int namelen = strlen(name);
  1097. zend_register_auto_global(name, namelen, NULL TSRMLS_CC);
  1098. zend_auto_global_disable_jit(name, namelen TSRMLS_CC);
  1099. ZEND_SET_SYMBOL(&EG(symbol_table), name, val);
  1100. }
  1101. void TPHPScriptingEngine::add_to_global_namespace(IDispatch *disp, DWORD flags, char *name TSRMLS_DC)
  1102. {
  1103. zval *val;
  1104. ITypeInfo *typ;
  1105. int i;
  1106. unsigned int namelen;
  1107. FUNCDESC *func;
  1108. BSTR funcname;
  1109. TYPEATTR *attr;
  1110. DISPPARAMS dispparams;
  1111. VARIANT vres;
  1112. ITypeInfo *rettyp;
  1113. TYPEATTR *retattr;
  1114. trace("Add %s to global namespace\n", name);
  1115. MAKE_STD_ZVAL(val);
  1116. php_com_wrap_dispatch(val, disp, CP_ACP TSRMLS_CC);
  1117. if (val == NULL) {
  1118. disp->Release();
  1119. return;
  1120. }
  1121. make_auto_global(name, val TSRMLS_CC);
  1122. if ((flags & SCRIPTITEM_GLOBALMEMBERS) == 0) {
  1123. disp->Release();
  1124. return;
  1125. }
  1126. /* Enumerate properties and add those too */
  1127. if (FAILED(disp->GetTypeInfo(0, 0, &typ))) {
  1128. disp->Release();
  1129. return;
  1130. }
  1131. if (SUCCEEDED(typ->GetTypeAttr(&attr))) {
  1132. for (i = 0; i < attr->cFuncs; i++) {
  1133. if (FAILED(typ->GetFuncDesc(i, &func)))
  1134. continue;
  1135. /* Look at its type */
  1136. if (func->invkind == INVOKE_PROPERTYGET
  1137. && VT_PTR == func->elemdescFunc.tdesc.vt
  1138. && VT_USERDEFINED == func->elemdescFunc.tdesc.lptdesc->vt
  1139. && SUCCEEDED(typ->GetRefTypeInfo(func->elemdescFunc.tdesc.lptdesc->hreftype, &rettyp)))
  1140. {
  1141. if (SUCCEEDED(rettyp->GetTypeAttr(&retattr))) {
  1142. if (retattr->typekind == TKIND_DISPATCH) {
  1143. /* It's dispatchable */
  1144. /* get the value */
  1145. dispparams.cArgs = 0;
  1146. dispparams.cNamedArgs = 0;
  1147. VariantInit(&vres);
  1148. if (SUCCEEDED(disp->Invoke(func->memid, IID_NULL, 0, func->invkind,
  1149. &dispparams, &vres, NULL, NULL))) {
  1150. /* Get its dispatch */
  1151. IDispatch *sub = NULL;
  1152. if (V_VT(&vres) == VT_UNKNOWN)
  1153. V_UNKNOWN(&vres)->QueryInterface(IID_IDispatch, (void**)&sub);
  1154. else if (V_VT(&vres) == VT_DISPATCH)
  1155. sub = V_DISPATCH(&vres);
  1156. if (sub) {
  1157. /* find out its name */
  1158. typ->GetDocumentation(func->memid, &funcname, NULL, NULL, NULL);
  1159. name = php_com_olestring_to_string(funcname, &namelen, CP_ACP, 0);
  1160. /* add to namespace */
  1161. zval *subval;
  1162. MAKE_STD_ZVAL(subval);
  1163. php_com_wrap_dispatch(subval, sub, CP_ACP TSRMLS_CC);
  1164. if (subval) {
  1165. make_auto_global(name, subval TSRMLS_CC);
  1166. }
  1167. efree(name);
  1168. SysFreeString(funcname);
  1169. }
  1170. VariantClear(&vres);
  1171. }
  1172. }
  1173. rettyp->ReleaseTypeAttr(retattr);
  1174. }
  1175. rettyp->Release();
  1176. }
  1177. typ->ReleaseFuncDesc(func);
  1178. }
  1179. typ->ReleaseTypeAttr(attr);
  1180. }
  1181. disp->Release();
  1182. }
  1183. STDMETHODIMP_(DWORD) TPHPScriptingEngine::AddRef(void)
  1184. {
  1185. return InterlockedIncrement(const_cast<long*> (&m_refcount));
  1186. }
  1187. STDMETHODIMP_(DWORD) TPHPScriptingEngine::Release(void)
  1188. {
  1189. DWORD ret = InterlockedDecrement(const_cast<long*> (&m_refcount));
  1190. if (ret == 0) {
  1191. trace("%08x: Release: zero refcount, destroy the engine!\n", this);
  1192. delete this;
  1193. }
  1194. return ret;
  1195. }
  1196. STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
  1197. {
  1198. *ppvObject = NULL;
  1199. if (IsEqualGUID(IID_IActiveScript, iid)) {
  1200. *ppvObject = (IActiveScript*)this;
  1201. } else if (IsEqualGUID(IID_IActiveScriptParse, iid)) {
  1202. *ppvObject = (IActiveScriptParse*)this;
  1203. } else if (IsEqualGUID(IID_IActiveScriptParseProcedure, iid)) {
  1204. *ppvObject = (IActiveScriptParseProcedure*)this;
  1205. #if ACTIVEPHP_OBJECT_SAFETY
  1206. } else if (IsEqualGUID(IID_IObjectSafety, iid)) {
  1207. *ppvObject = (IObjectSafety*)this;
  1208. #endif
  1209. } else if (IsEqualGUID(IID_IUnknown, iid)) {
  1210. *ppvObject = this;
  1211. } else {
  1212. LPOLESTR guidw;
  1213. StringFromCLSID(iid, &guidw);
  1214. {
  1215. TWideString guid(guidw);
  1216. trace("%08x: QueryInterface for unsupported %s\n", this, guid.ansi_string());
  1217. }
  1218. CoTaskMemFree(guidw);
  1219. }
  1220. if (*ppvObject) {
  1221. AddRef();
  1222. return S_OK;
  1223. }
  1224. return E_NOINTERFACE;
  1225. }
  1226. /* This is called by the host to set the scrite site.
  1227. * It also defines the base thread. */
  1228. STDMETHODIMP TPHPScriptingEngine::SetScriptSite(IActiveScriptSite *pass)
  1229. {
  1230. TSRMLS_FETCH();
  1231. tsrm_mutex_lock(m_mutex);
  1232. trace("%08x: -----> Base thread is %08x\n", this, tsrm_thread_id());
  1233. if (m_pass) {
  1234. m_pass->Release();
  1235. m_pass = NULL;
  1236. SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, 0 TSRMLS_CC);
  1237. }
  1238. if (pass == NULL) {
  1239. trace("Closing down site; we should have no references to objects from the host\n"
  1240. " m_pass=%08x\n m_pass_eng=%08x\n What about named items??\n",
  1241. m_pass, m_pass_eng);
  1242. }
  1243. m_pass = pass;
  1244. if (m_pass) {
  1245. m_pass->AddRef();
  1246. DWORD cookie;
  1247. if (SUCCEEDED(GIT_put(m_pass, IID_IActiveScriptSite, &cookie)))
  1248. SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, cookie TSRMLS_CC);
  1249. if (m_scriptstate == SCRIPTSTATE_UNINITIALIZED)
  1250. SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, SCRIPTSTATE_INITIALIZED TSRMLS_CC);
  1251. }
  1252. tsrm_mutex_unlock(m_mutex);
  1253. return S_OK;
  1254. }
  1255. STDMETHODIMP TPHPScriptingEngine::GetScriptSite(REFIID riid, void **ppvObject)
  1256. {
  1257. HRESULT ret = S_FALSE;
  1258. trace("%08x: GetScriptSite()\n", this);
  1259. tsrm_mutex_lock(m_mutex);
  1260. if (m_pass)
  1261. ret = m_pass->QueryInterface(riid, ppvObject);
  1262. tsrm_mutex_unlock(m_mutex);
  1263. return ret;
  1264. }
  1265. STDMETHODIMP TPHPScriptingEngine::SetScriptState(SCRIPTSTATE ss)
  1266. {
  1267. TSRMLS_FETCH();
  1268. trace("%08x: SetScriptState(%s)\n", this, scriptstate_to_string(ss));
  1269. return SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, ss TSRMLS_CC);
  1270. }
  1271. STDMETHODIMP TPHPScriptingEngine::GetScriptState(SCRIPTSTATE *pssState)
  1272. {
  1273. trace("%08x: GetScriptState(current=%s)\n", this, scriptstate_to_string(m_scriptstate));
  1274. tsrm_mutex_lock(m_mutex);
  1275. *pssState = m_scriptstate;
  1276. tsrm_mutex_unlock(m_mutex);
  1277. return S_OK;
  1278. }
  1279. STDMETHODIMP TPHPScriptingEngine::Close(void)
  1280. {
  1281. TSRMLS_FETCH();
  1282. if (m_pass) {
  1283. m_pass->Release();
  1284. m_pass = NULL;
  1285. }
  1286. SEND_THREAD_MESSAGE(this, PHPSE_CLOSE, 0, 0 TSRMLS_CC);
  1287. return S_OK;
  1288. }
  1289. /* Add an item to global namespace.
  1290. * This is called in the context of the base thread (or perhaps some other thread).
  1291. * We want to be able to work with the object in the engine thread, so we marshal
  1292. * it into a stream and let the engine thread deal with it.
  1293. * This works quite nicely when PHP scripts call into the object; threading is
  1294. * handled correctly. */
  1295. STDMETHODIMP TPHPScriptingEngine::AddNamedItem(LPCOLESTR pstrName, DWORD dwFlags)
  1296. {
  1297. struct php_active_script_add_named_item_info info;
  1298. HRESULT res;
  1299. TSRMLS_FETCH();
  1300. info.pstrName = pstrName;
  1301. info.dwFlags = dwFlags;
  1302. res = m_pass->GetItemInfo(pstrName, SCRIPTINFO_IUNKNOWN, &info.punk, NULL);
  1303. if (SUCCEEDED(res)) {
  1304. #if ACTIVEPHP_THREADING_MODE == COINIT_MULTITHREADED
  1305. /* strangely, the GIT doesn't want to give the engine thread the interface
  1306. * in this threading mode */
  1307. SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
  1308. #else
  1309. IDispatch *disp;
  1310. res = info.punk->QueryInterface(IID_IDispatch, (void**)&disp);
  1311. if (SUCCEEDED(res) && disp) {
  1312. if (SUCCEEDED(GIT_put(disp, IID_IDispatch, &info.marshal))) {
  1313. trace("put disp=%p into git with marshal ID of %x\n", disp, info.marshal);
  1314. SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
  1315. GIT_revoke(info.marshal, disp);
  1316. }
  1317. disp->Release();
  1318. } else {
  1319. trace("failed to get IDispatch from punk %s", php_win_err(res));
  1320. }
  1321. info.punk->Release();
  1322. #endif
  1323. } else {
  1324. trace("failed to get named item, %s", php_win_err(res));
  1325. }
  1326. return S_OK;
  1327. }
  1328. /* Bind to a type library */
  1329. STDMETHODIMP TPHPScriptingEngine::AddTypeLib(
  1330. /* [in] */ REFGUID rguidTypeLib,
  1331. /* [in] */ DWORD dwMajor,
  1332. /* [in] */ DWORD dwMinor,
  1333. /* [in] */ DWORD dwFlags)
  1334. {
  1335. struct php_active_script_add_tlb_info info;
  1336. TSRMLS_FETCH();
  1337. info.rguidTypeLib = &rguidTypeLib;
  1338. info.dwMajor = dwMajor;
  1339. info.dwMinor = dwMinor;
  1340. info.dwFlags = dwFlags;
  1341. SEND_THREAD_MESSAGE(this, PHPSE_ADD_TYPELIB, 0, (LPARAM)&info TSRMLS_CC);
  1342. return S_OK;
  1343. }
  1344. /* Returns an object representing the PHP Scripting Engine.
  1345. * Optionally, a client can request a particular item directly.
  1346. * For the moment, we only do the bare minimum amount of work
  1347. * for the engine to work correctly; we can flesh out this part
  1348. * a little later. */
  1349. STDMETHODIMP TPHPScriptingEngine::GetScriptDispatch(
  1350. /* [in] */ LPCOLESTR pstrItemName,
  1351. /* [out] */ IDispatch **ppdisp)
  1352. {
  1353. TSRMLS_FETCH();
  1354. *ppdisp = NULL;
  1355. struct php_active_script_get_dispatch_info info;
  1356. info.pstrItemName = pstrItemName;
  1357. info.dispatch = NULL;
  1358. /* This hack is required because the host is likely to query us
  1359. * for a dispatch if we use any of its objects from PHP script.
  1360. * Since the engine thread will be waiting for the return from
  1361. * a COM call, we need to deliberately poke a hole in thread
  1362. * safety so that it is possible to read the symbol table from
  1363. * outside the engine thread and give it a valid return value.
  1364. * This is "safe" only in this instance, since we are not modifying
  1365. * the engine state by looking up the dispatch (I hope).
  1366. * The scripting engine rules pretty much guarantee that this
  1367. * method is only called in the base thread.
  1368. * This appears to only happen when we set the threading to
  1369. * apartment. */
  1370. if (tsrm_thread_id() != m_enginethread) {
  1371. tsrm_ls = m_tsrm_hack;
  1372. trace("HEY: hacking thread safety!\n");
  1373. }
  1374. if (S_OK == engine_thread_handler(PHPSE_GET_DISPATCH, 0, (LPARAM)&info, NULL TSRMLS_CC)) {
  1375. GIT_get(info.dispatch, IID_IDispatch, (void**)ppdisp);
  1376. }
  1377. if (*ppdisp) {
  1378. return S_OK;
  1379. }
  1380. return S_FALSE;
  1381. }
  1382. STDMETHODIMP TPHPScriptingEngine::GetCurrentScriptThreadID(
  1383. /* [out] */ SCRIPTTHREADID *pstidThread)
  1384. {
  1385. // tsrm_mutex_lock(m_mutex);
  1386. trace("%08x: GetCurrentScriptThreadID()\n", this);
  1387. *pstidThread = m_enginethread;
  1388. // tsrm_mutex_unlock(m_mutex);
  1389. return S_OK;
  1390. }
  1391. STDMETHODIMP TPHPScriptingEngine::GetScriptThreadID(
  1392. /* [in] */ DWORD dwWin32ThreadId,
  1393. /* [out] */ SCRIPTTHREADID *pstidThread)
  1394. {
  1395. // tsrm_mutex_lock(m_mutex);
  1396. trace("%08x: GetScriptThreadID()\n", this);
  1397. *pstidThread = dwWin32ThreadId;
  1398. // tsrm_mutex_unlock(m_mutex);
  1399. return S_OK;
  1400. }
  1401. STDMETHODIMP TPHPScriptingEngine::GetScriptThreadState(
  1402. /* [in] */ SCRIPTTHREADID stidThread,
  1403. /* [out] */ SCRIPTTHREADSTATE *pstsState)
  1404. {
  1405. // tsrm_mutex_lock(m_mutex);
  1406. trace("%08x: GetScriptThreadState()\n", this);
  1407. *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
  1408. switch(stidThread) {
  1409. case SCRIPTTHREADID_BASE:
  1410. stidThread = m_basethread;
  1411. break;
  1412. case SCRIPTTHREADID_CURRENT:
  1413. stidThread = m_enginethread;
  1414. break;
  1415. };
  1416. if (stidThread == m_basethread) {
  1417. *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
  1418. } else if (stidThread == m_enginethread) {
  1419. *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
  1420. }
  1421. // tsrm_mutex_unlock(m_mutex);
  1422. return S_OK;
  1423. }
  1424. STDMETHODIMP TPHPScriptingEngine::InterruptScriptThread(
  1425. /* [in] */ SCRIPTTHREADID stidThread,
  1426. /* [in] */ const EXCEPINFO *pexcepinfo,
  1427. /* [in] */ DWORD dwFlags)
  1428. {
  1429. /* do not serialize this method, or call into the script site */
  1430. trace("%08x: InterruptScriptThread()\n", this);
  1431. return S_OK;
  1432. }
  1433. /* Clone is essential when running under Windows Script Host.
  1434. * It creates an engine to parse the code. Once it is parsed,
  1435. * the host clones other engines from the original and runs those.
  1436. * It is intended to be a fast method of running the same script
  1437. * multiple times in multiple threads. */
  1438. STDMETHODIMP TPHPScriptingEngine::Clone(
  1439. /* [out] */ IActiveScript **ppscript)
  1440. {
  1441. TPHPScriptingEngine *cloned = new TPHPScriptingEngine;
  1442. TSRMLS_FETCH();
  1443. trace("%08x: Clone()\n", this);
  1444. if (ppscript)
  1445. *ppscript = NULL;
  1446. if (cloned) {
  1447. cloned->InitNew();
  1448. SEND_THREAD_MESSAGE(cloned, PHPSE_CLONE, 0, (LPARAM)this TSRMLS_CC);
  1449. trace("%08x: Cloned OK, returning cloned object ptr %08x\n", this, cloned);
  1450. *ppscript = (IActiveScript*)cloned;
  1451. return S_OK;
  1452. }
  1453. return E_FAIL;
  1454. }
  1455. STDMETHODIMP TPHPScriptingEngine::InitNew( void)
  1456. {
  1457. TSRMLS_FETCH();
  1458. SEND_THREAD_MESSAGE(this, PHPSE_INIT_NEW, 0, 0 TSRMLS_CC);
  1459. return S_OK;
  1460. }
  1461. STDMETHODIMP TPHPScriptingEngine::AddScriptlet(
  1462. /* [in] */ LPCOLESTR pstrDefaultName,
  1463. /* [in] */ LPCOLESTR pstrCode,
  1464. /* [in] */ LPCOLESTR pstrItemName,
  1465. /* [in] */ LPCOLESTR pstrSubItemName,
  1466. /* [in] */ LPCOLESTR pstrEventName,
  1467. /* [in] */ LPCOLESTR pstrDelimiter,
  1468. /* [in] */ DWORD dwSourceContextCookie,
  1469. /* [in] */ ULONG ulStartingLineNumber,
  1470. /* [in] */ DWORD dwFlags,
  1471. /* [out] */ BSTR *pbstrName,
  1472. /* [out] */ EXCEPINFO *pexcepinfo)
  1473. {
  1474. struct php_active_script_add_scriptlet_info info;
  1475. TSRMLS_FETCH();
  1476. info.pstrDefaultName = pstrDefaultName;
  1477. info.pstrCode = pstrCode;
  1478. info.pstrItemName = pstrItemName;
  1479. info.pstrSubItemName = pstrSubItemName;
  1480. info.pstrEventName = pstrEventName;
  1481. info.pstrDelimiter = pstrDelimiter;
  1482. info.dwSourceContextCookie = dwSourceContextCookie;
  1483. info.ulStartingLineNumber = ulStartingLineNumber;
  1484. info.dwFlags = dwFlags;
  1485. info.pbstrName = pbstrName;
  1486. info.pexcepinfo = pexcepinfo;
  1487. return SEND_THREAD_MESSAGE(this, PHPSE_ADD_SCRIPTLET, 0, (LPARAM)&info TSRMLS_CC);
  1488. }
  1489. STDMETHODIMP TPHPScriptingEngine::ParseScriptText(
  1490. /* [in] */ LPCOLESTR pstrCode,
  1491. /* [in] */ LPCOLESTR pstrItemName,
  1492. /* [in] */ IUnknown *punkContext,
  1493. /* [in] */ LPCOLESTR pstrDelimiter,
  1494. /* [in] */ DWORD dwSourceContextCookie,
  1495. /* [in] */ ULONG ulStartingLineNumber,
  1496. /* [in] */ DWORD dwFlags,
  1497. /* [out] */ VARIANT *pvarResult,
  1498. /* [out] */ EXCEPINFO *pexcepinfo)
  1499. {
  1500. struct php_active_script_parse_info info;
  1501. TSRMLS_FETCH();
  1502. info.pstrCode = pstrCode;
  1503. info.pstrItemName = pstrItemName;
  1504. info.punkContext = punkContext;
  1505. info.pstrDelimiter = pstrDelimiter;
  1506. info.dwSourceContextCookie = dwSourceContextCookie;
  1507. info.ulStartingLineNumber = ulStartingLineNumber;
  1508. info.dwFlags = dwFlags;
  1509. info.pvarResult = pvarResult;
  1510. info.pexcepinfo = pexcepinfo;
  1511. return SEND_THREAD_MESSAGE(this, PHPSE_PARSE_SCRIPT, 0, (LPARAM)&info TSRMLS_CC);
  1512. }
  1513. STDMETHODIMP TPHPScriptingEngine::ParseProcedureText(
  1514. /* [in] */ LPCOLESTR pstrCode,
  1515. /* [in] */ LPCOLESTR pstrFormalParams,
  1516. /* [in] */ LPCOLESTR pstrProcedureName,
  1517. /* [in] */ LPCOLESTR pstrItemName,
  1518. /* [in] */ IUnknown *punkContext,
  1519. /* [in] */ LPCOLESTR pstrDelimiter,
  1520. /* [in] */ DWORD dwSourceContextCookie,
  1521. /* [in] */ ULONG ulStartingLineNumber,
  1522. /* [in] */ DWORD dwFlags,
  1523. /* [out] */ IDispatch **ppdisp)
  1524. {
  1525. struct php_active_script_parse_proc_info info;
  1526. HRESULT ret;
  1527. TSRMLS_FETCH();
  1528. info.pstrCode = pstrCode;
  1529. info.pstrFormalParams = pstrFormalParams;
  1530. info.pstrProcedureName = pstrProcedureName;
  1531. info.pstrItemName = pstrItemName;
  1532. info.punkContext = punkContext;
  1533. info.pstrDelimiter = pstrDelimiter;
  1534. info.dwSourceContextCookie = dwSourceContextCookie;
  1535. info.ulStartingLineNumber = ulStartingLineNumber;
  1536. info.dwFlags = dwFlags;
  1537. ret = SEND_THREAD_MESSAGE(this, PHPSE_PARSE_PROC, 0, (LPARAM)&info TSRMLS_CC);
  1538. if (ret == S_OK)
  1539. ret = GIT_get(info.dispcookie, IID_IDispatch, (void**)ppdisp);
  1540. trace("ParseProc: ret=%08x disp=%08x\n", ret, *ppdisp);
  1541. return ret;
  1542. }
  1543. #if ACTIVEPHP_OBJECT_SAFETY
  1544. STDMETHODIMP TPHPScriptingEngine::GetInterfaceSafetyOptions(
  1545. /* [in] */ REFIID riid, // Interface that we want options for
  1546. /* [out] */ DWORD *pdwSupportedOptions, // Options meaningful on this interface
  1547. /* [out] */ DWORD *pdwEnabledOptions) // current option values on this interface
  1548. {
  1549. /* PHP isn't really safe for untrusted anything */
  1550. *pdwSupportedOptions = 0;
  1551. *pdwEnabledOptions = 0;
  1552. return S_OK;
  1553. }
  1554. STDMETHODIMP TPHPScriptingEngine::SetInterfaceSafetyOptions(
  1555. /* [in] */ REFIID riid, // Interface to set options for
  1556. /* [in] */ DWORD dwOptionSetMask, // Options to change
  1557. /* [in] */ DWORD dwEnabledOptions) // New option values
  1558. {
  1559. /* PHP isn't really safe for untrusted anything */
  1560. if (dwEnabledOptions == 0) {
  1561. return S_OK;
  1562. }
  1563. return E_FAIL;
  1564. }
  1565. #endif
  1566. extern "C"
  1567. void activescript_error_func(int type, const char *error_msg, ...)
  1568. {
  1569. TSRMLS_FETCH();
  1570. TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
  1571. }
  1572. class TActiveScriptError:
  1573. public IActiveScriptError
  1574. {
  1575. protected:
  1576. volatile LONG m_refcount;
  1577. public:
  1578. /* IUnknown */
  1579. STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject) {
  1580. *ppvObject = NULL;
  1581. if (IsEqualGUID(IID_IActiveScriptError, iid)) {
  1582. *ppvObject = (IActiveScriptError*)this;
  1583. } else if (IsEqualGUID(IID_IUnknown, iid)) {
  1584. *ppvObject = this;
  1585. }
  1586. if (*ppvObject) {
  1587. AddRef();
  1588. return S_OK;
  1589. }
  1590. return E_NOINTERFACE;
  1591. }
  1592. STDMETHODIMP_(DWORD) AddRef(void) {
  1593. return InterlockedIncrement(const_cast<long*> (&m_refcount));
  1594. }
  1595. STDMETHODIMP_(DWORD) Release(void) {
  1596. DWORD ret = InterlockedDecrement(const_cast<long*> (&m_refcount));
  1597. trace("Release: errobj refcount=%d\n", ret);
  1598. if (ret == 0)
  1599. delete this;
  1600. return ret;
  1601. }
  1602. HRESULT STDMETHODCALLTYPE GetExceptionInfo(
  1603. /* [out] */ EXCEPINFO *pexcepinfo)
  1604. {
  1605. memset(pexcepinfo, 0, sizeof(EXCEPINFO));
  1606. pexcepinfo->bstrDescription = SysAllocString(m_message);
  1607. pexcepinfo->bstrSource = SysAllocString(m_filename);
  1608. pexcepinfo->wCode = 1000;
  1609. return S_OK;
  1610. }
  1611. HRESULT STDMETHODCALLTYPE GetSourcePosition(
  1612. /* [out] */ DWORD *pdwSourceContext,
  1613. /* [out] */ ULONG *pulLineNumber,
  1614. /* [out] */ LONG *plCharacterPosition)
  1615. {
  1616. *pdwSourceContext = 0;
  1617. *pulLineNumber = m_lineno;
  1618. *plCharacterPosition = 0;
  1619. return S_OK;
  1620. }
  1621. HRESULT STDMETHODCALLTYPE GetSourceLineText(
  1622. /* [out] */ BSTR *pbstrSourceLine)
  1623. {
  1624. *pbstrSourceLine = NULL;
  1625. return E_FAIL;
  1626. }
  1627. BSTR m_filename, m_message;
  1628. UINT m_lineno;
  1629. TActiveScriptError(const char *filename, const uint lineno, const char *message)
  1630. {
  1631. m_refcount = 0; /* start with zero refcount because this object is passed
  1632. * directly to the script site; it will call addref */
  1633. m_filename = TWideString::bstr_from_ansi((char*)filename);
  1634. m_message = TWideString::bstr_from_ansi((char*)message);
  1635. m_lineno = lineno;
  1636. }
  1637. ~TActiveScriptError()
  1638. {
  1639. trace("%08x: cleaning up error object\n", this);
  1640. SysFreeString(m_filename);
  1641. SysFreeString(m_message);
  1642. }
  1643. };
  1644. extern "C"
  1645. void activescript_error_handler(int type, const char *error_filename,
  1646. const uint error_lineno, const char *format, va_list args)
  1647. {
  1648. TSRMLS_FETCH();
  1649. char *buf;
  1650. int buflen;
  1651. TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
  1652. buflen = vspprintf(&buf, PG(log_errors_max_len), format, args);
  1653. trace("%08x: Error: %s\n", engine, buf);
  1654. /* if it's a fatal error, report it using IActiveScriptError. */
  1655. switch(type) {
  1656. case E_ERROR:
  1657. case E_CORE_ERROR:
  1658. case E_COMPILE_ERROR:
  1659. case E_USER_ERROR:
  1660. case E_PARSE:
  1661. /* trigger an error in the host */
  1662. TActiveScriptError *eobj = new TActiveScriptError(error_filename, error_lineno, buf);
  1663. trace("raising error object!\n");
  1664. if (engine->m_pass_eng)
  1665. engine->m_pass_eng->OnScriptError(eobj);
  1666. /* now throw the exception to abort execution */
  1667. if (engine->m_err_trap)
  1668. longjmp(*engine->m_err_trap, 1);
  1669. break;
  1670. }
  1671. efree(buf);
  1672. }