summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sapi/activescript/README9
-rw-r--r--sapi/activescript/classfactory.cpp20
-rw-r--r--sapi/activescript/config.w322
-rwxr-xr-xsapi/activescript/marshal.cpp406
-rw-r--r--sapi/activescript/php5activescript.c1
-rw-r--r--sapi/activescript/php5as_classfactory.h4
-rw-r--r--sapi/activescript/php5as_scriptengine.h158
-rw-r--r--sapi/activescript/scriptengine.cpp1735
8 files changed, 1359 insertions, 976 deletions
diff --git a/sapi/activescript/README b/sapi/activescript/README
index 39066f5783..fc889d9e1b 100644
--- a/sapi/activescript/README
+++ b/sapi/activescript/README
@@ -4,7 +4,6 @@ This is the ActiveScript SAPI for PHP.
Once registered on your system (using regsvr32), you will be able to use
PHP script in any ActiveScript compliant host. The list includes:
-o. Client-side script in Internet Explorer
o. Windows Script Host
o. ASP and ASP.NET
o. Windows Script Components / Behaviours
@@ -24,12 +23,6 @@ Build and install it somewhere; then register the engine like this:
Usage.
======
-o. Client-side script in Internet Explorer
-
- <script language="ActivePHP5">
- $window->alert("Hello");
- </script>
-
o. Windows Script Host
Create a .wsf file like this:
@@ -38,7 +31,7 @@ o. Windows Script Host
<script language="ActivePHP5">
$WScript->Echo("Hello");
</script>
- </script>
+ </job>
o. ASP and ASP.NET
diff --git a/sapi/activescript/classfactory.cpp b/sapi/activescript/classfactory.cpp
index bd0416d7ed..5fa37c9f03 100644
--- a/sapi/activescript/classfactory.cpp
+++ b/sapi/activescript/classfactory.cpp
@@ -98,11 +98,15 @@ STDMETHODIMP TPHPClassFactory::LockServer(BOOL fLock)
STDMETHODIMP TPHPClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppvObject)
{
- TPHPScriptingEngine *engine = new TPHPScriptingEngine;
+ IUnknown *punk = create_scripting_engine(NULL);
+ HRESULT ret;
- HRESULT ret = engine->QueryInterface(iid, ppvObject);
-
- engine->Release();
+ if (punk) {
+ ret = punk->QueryInterface(iid, ppvObject);
+ punk->Release();
+ } else {
+ ret = E_UNEXPECTED;
+ }
return ret;
}
@@ -161,7 +165,13 @@ static const GUID *script_engine_categories[] = {
};
static const struct reg_class classes_to_register[] = {
- { &CLSID_PHPActiveScriptEngine, "PHP Active Script Engine", "Both", engine_entries, script_engine_categories },
+ { &CLSID_PHPActiveScriptEngine, "PHP Active Script Engine",
+#if ACTIVEPHP_THREADING_MODE == COINIT_MULTITHREADED
+ "Both",
+#else
+ "Apartment",
+#endif
+ engine_entries, script_engine_categories },
{ NULL, NULL, NULL, 0, NULL }
};
/* }}} */
diff --git a/sapi/activescript/config.w32 b/sapi/activescript/config.w32
index 401f8663a6..0dbfc5851c 100644
--- a/sapi/activescript/config.w32
+++ b/sapi/activescript/config.w32
@@ -8,6 +8,6 @@ if (PHP_ACTIVESCRIPT == "yes") {
ERROR("ActiveScript module requires an --enable-zts build of PHP");
}
- SAPI('activescript', 'classfactory.cpp php5activescript.c scriptengine.cpp', 'php' + PHP_VERSION + 'activescript.dll', '/D PHP5ISAPI_EXPORTS /D ACTIVEPHP_OBJECT_SAFETY=1');
+ SAPI('activescript', 'classfactory.cpp php5activescript.c scriptengine.cpp marshal.cpp', 'php' + PHP_VERSION + 'activescript.dll', '/D PHP5ISAPI_EXPORTS /D ACTIVEPHP_OBJECT_SAFETY=1');
ADD_FLAG('LDFLAGS_ACTIVESCRIPT', 'oleaut32.lib ole32.lib user32.lib advapi32.lib /DEF:' + configure_module_dirname + '\\php5activescript.def');
}
diff --git a/sapi/activescript/marshal.cpp b/sapi/activescript/marshal.cpp
new file mode 100755
index 0000000000..2d4c61cb87
--- /dev/null
+++ b/sapi/activescript/marshal.cpp
@@ -0,0 +1,406 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2004 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.0 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_0.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Wez Furlong <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+/* Fun with threads */
+
+#define _WIN32_DCOM
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+#include <winsock2.h>
+#include "php5as_scriptengine.h"
+#include "php5as_classfactory.h"
+#include <objbase.h>
+#undef php_win_err
+
+extern "C" char *php_win_err(HRESULT ret);
+
+#define APHPM_IN 1
+#define APHPM_OUT 2
+
+#define APHPT_TERM 0
+#define APHPT_UNK 1 /* IUnknown * */
+#define APHPT_DISP 2 /* IDispatch * */
+#define APHPT_VAR 3 /* PVARIANT */
+
+static inline void trace(char *fmt, ...)
+{
+ va_list ap;
+ char buf[4096];
+
+ sprintf(buf, "T=%08x [MARSHAL] ", tsrm_thread_id());
+ OutputDebugString(buf);
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+
+ OutputDebugString(buf);
+
+ va_end(ap);
+}
+struct marshal_arg {
+ int type;
+ int argno;
+ int direction;
+};
+
+static int parse_script_text_mdef[] = {
+ APHPT_UNK, 2, APHPM_IN,
+ APHPT_VAR, 7, APHPM_OUT,
+ APHPT_TERM
+};
+
+static int get_script_dispatch_mdef[] = {
+ APHPT_DISP, 1, APHPM_OUT,
+ APHPT_TERM
+};
+
+static int *mdef_by_func[APHP__Max] = {
+ parse_script_text_mdef,
+ NULL, /* InitNew */
+ NULL, /* AddNamedItem */
+ NULL, /* SetScriptState */
+ get_script_dispatch_mdef,
+ NULL, /* Close */
+ NULL, /* AddTypeLib */
+ NULL, /* AddScriptlet */
+};
+
+static HRESULT do_marshal_in(int stub, void *args[16], int *mdef, LPSTREAM *ppstm)
+{
+ int i = 0;
+ int want;
+ HRESULT ret = S_OK;
+ LPSTREAM stm = NULL;
+
+ if (!mdef)
+ return S_OK;
+
+ trace("marshalling ... \n");
+
+ ret = CreateStreamOnHGlobal(NULL, TRUE, &stm);
+ if (FAILED(ret)) {
+ trace(" failed to create stm %s", php_win_err(ret));
+ return ret;
+ }
+
+ *ppstm = stm;
+
+ /* if stub is true, we are the stub and are marshaling OUT params,
+ * otherwise, we are the proxy and are marshalling IN params */
+
+ if (stub) {
+ want = APHPM_OUT;
+ } else {
+ want = APHPM_IN;
+ }
+
+ while (mdef[i] != APHPT_TERM) {
+ if ((mdef[i+2] & want) == want) {
+ int argno = mdef[i+1];
+ int isout = (mdef[i+2] & APHPM_OUT) == APHPM_OUT;
+
+#undef OUT_IFACE
+#define OUT_IFACE (isout ? *(IUnknown**)args[argno] : (IUnknown*)args[argno])
+#define IFACE_PRESENT args[argno] && (!isout || *(IUnknown**)args[argno])
+ switch (mdef[i]) {
+ case APHPT_UNK:
+ if (IFACE_PRESENT) {
+ ret = CoMarshalInterface(stm, IID_IUnknown, OUT_IFACE, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+ trace(" arg=%d IUnknown --> %s", argno, php_win_err(ret));
+ } else {
+ trace(" arg=%d IUnknown(NULL) - skip\n", argno);
+ }
+ break;
+
+ case APHPT_DISP:
+ if (IFACE_PRESENT) {
+ ret = CoMarshalInterface(stm, IID_IDispatch, OUT_IFACE, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+ trace(" arg=%d IDispatch --> %s", argno, php_win_err(ret));
+ } else {
+ trace(" arg=%d IDispatch(NULL) - skip\n", argno);
+ }
+ break;
+
+ case APHPT_VAR:
+ if (args[argno])
+ ret = E_NOTIMPL;
+ break;
+
+ default:
+ ret = E_NOTIMPL;
+ }
+
+ if (FAILED(ret))
+ break;
+ } else {
+ trace(" -- skipping (this param is not needed in this direction)\n");
+ }
+ i += 3;
+ }
+
+ if (FAILED(ret)) {
+ /* TODO: rollback (refcounts are held during marshalling) */
+ trace(" rolling back\n");
+ stm->Release();
+ *ppstm = NULL;
+ } else {
+ LARGE_INTEGER pos = {0};
+ stm->Seek(pos, STREAM_SEEK_SET, NULL);
+ }
+
+ return ret;
+}
+
+static HRESULT do_marshal_out(int stub, void *args[16], int *mdef, LPSTREAM stm)
+{
+ int i = 0;
+ int want;
+ HRESULT ret = S_OK;
+
+ if (!mdef)
+ return S_OK;
+
+ trace(" unmarshalling...\n");
+
+ /* if stub is true, we are the stub and are unmarshaling IN params,
+ * otherwise, we are the proxy and are unmarshalling OUT params */
+
+ if (!stub) {
+ want = APHPM_OUT;
+ } else {
+ want = APHPM_IN;
+ }
+
+ while (mdef[i] != APHPT_TERM) {
+ if ((mdef[i+2] & want) == want) {
+ int argno = mdef[i+1];
+ int isout = (mdef[i+2] & APHPM_OUT) == APHPM_OUT;
+#undef OUT_IFACE
+#define OUT_IFACE (isout ? (void**)args[argno] : &args[argno])
+
+ switch (mdef[i]) {
+ case APHPT_UNK:
+ if (IFACE_PRESENT) {
+ ret = CoUnmarshalInterface(stm, IID_IUnknown, OUT_IFACE);
+ trace(" unmarshal arg=%d IUnknown --> %s", argno, php_win_err(ret));
+ } else {
+ trace(" unmarshal arg=%d IUnknown(NULL) - skip\n", argno);
+ }
+ break;
+
+ case APHPT_DISP:
+ if (IFACE_PRESENT) {
+ trace(" unmarshal dispatch: args[%d]=%p *args[%d]=%p\n",
+ argno, args[argno], argno, args[argno] ? *(void**)args[argno] : NULL);
+ ret = CoUnmarshalInterface(stm, IID_IDispatch, OUT_IFACE);
+ trace(" unmarshal arg=%d IDispatch --> %s: args[%d]=%p *args[%d]=%p\n", argno, php_win_err(ret),
+ argno, args[argno], argno, args[argno] ? *(void**)args[argno] : NULL);
+ } else {
+ trace(" unmarshal arg=%d IDispatch(NULL) - skip\n", argno);
+ }
+ break;
+
+ case APHPT_VAR:
+ if (args[argno])
+ ret = E_NOTIMPL;
+ break;
+
+ default:
+ ret = E_NOTIMPL;
+ }
+ if (FAILED(ret))
+ break;
+ }
+ i += 3;
+ }
+
+ return ret;
+}
+
+
+struct activephp_serialize_msg {
+ class TPHPScriptingEngine *engine;
+ void *args[16];
+ int nargs;
+ enum activephp_engine_func func;
+ int *marshal_defs;
+ LPSTREAM instm, outstm;
+
+ HANDLE evt;
+ HRESULT ret;
+};
+
+static const char *func_names[APHP__Max] = {
+ "ParseScriptText",
+ "InitNew",
+ "AddnamedItem",
+ "SetScriptState",
+ "GetScriptDispatch",
+ "Close",
+ "AddTypeLib",
+ "AddScriptlet",
+};
+
+HRESULT marshal_call(class TPHPScriptingEngine *engine, enum activephp_engine_func func, int nargs, ...)
+{
+ va_list ap;
+ struct activephp_serialize_msg msg ;
+ HRESULT ret;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.engine = engine;
+ msg.func = func;
+ msg.marshal_defs = mdef_by_func[func];
+
+ trace(" prepping for function code %d %s, %d args, marshal defs at %p\n", func, func_names[func], nargs, msg.marshal_defs);
+
+ va_start(ap, nargs);
+ for (msg.nargs = 0; msg.nargs < nargs; msg.nargs++) {
+ msg.args[msg.nargs] = va_arg(ap, void*);
+ }
+ va_end(ap);
+
+ ret = do_marshal_in(0, msg.args, msg.marshal_defs, &msg.instm);
+
+ if (FAILED(ret)) {
+ return ret;
+ }
+
+#if 1
+ msg.evt = CreateEvent(NULL, TRUE, FALSE, NULL);
+ PostMessage(engine->m_queue, WM_ACTIVEPHP_SERIALIZE, 0, (LPARAM)&msg);
+
+ while (WAIT_OBJECT_0 != WaitForSingleObject(msg.evt, 0)) {
+ DWORD status = MsgWaitForMultipleObjects(1, &msg.evt, FALSE, INFINITE, QS_ALLEVENTS|QS_ALLINPUT|QS_ALLPOSTMESSAGE|QS_SENDMESSAGE|QS_POSTMESSAGE);
+
+ if (status == WAIT_OBJECT_0)
+ break;
+
+ MSG msg;
+
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ CloseHandle(msg.evt);
+#else
+ ret = SendMessage(engine->m_queue, WM_ACTIVEPHP_SERIALIZE, 0, (LPARAM)&msg);
+#endif
+
+ if (msg.outstm) {
+ ret = do_marshal_out(0, msg.args, msg.marshal_defs, msg.outstm);
+ msg.outstm->Release();
+ }
+
+ if (msg.instm)
+ msg.instm->Release();
+
+ trace("marshall call to %s completed %s", func_names[func], php_win_err(ret));
+
+ return ret;
+}
+
+HRESULT marshal_stub(LPARAM lparam)
+{
+ struct activephp_serialize_msg *msg = (struct activephp_serialize_msg*)lparam;
+
+ if (msg->instm) {
+ msg->ret = do_marshal_out(1, msg->args, msg->marshal_defs, msg->instm);
+
+ if (FAILED(msg->ret)) {
+ SetEvent(msg->evt);
+ return msg->ret;
+ }
+ }
+
+ switch (msg->func) {
+ case APHP_ParseScriptText:
+ msg->ret = msg->engine->ParseScriptText(
+ (LPCOLESTR)msg->args[0],
+ (LPCOLESTR)msg->args[1],
+ (IUnknown*)msg->args[2],
+ (LPCOLESTR)msg->args[3],
+ (DWORD)msg->args[4],
+ (ULONG)msg->args[5],
+ (DWORD)msg->args[6],
+ (VARIANT*)msg->args[7],
+ (EXCEPINFO*)msg->args[8]);
+ break;
+
+ case APHP_InitNew:
+ msg->ret = msg->engine->InitNew();
+ break;
+
+ case APHP_AddNamedItem:
+ msg->ret = msg->engine->AddNamedItem(
+ (LPCOLESTR)msg->args[0],
+ (DWORD)msg->args[1]);
+ break;
+
+ case APHP_SetScriptState:
+ msg->ret = msg->engine->SetScriptState((SCRIPTSTATE)(LONG)msg->args[0]);
+ break;
+
+ case APHP_GetScriptDispatch:
+ msg->ret = msg->engine->GetScriptDispatch(
+ (LPCOLESTR)msg->args[0],
+ (IDispatch**)msg->args[1]);
+ break;
+
+ case APHP_Close:
+ msg->ret = msg->engine->Close();
+ break;
+
+ case APHP_AddTypeLib:
+ msg->ret = msg->engine->AddTypeLib(
+ (REFGUID)msg->args[0],
+ (DWORD)msg->args[1],
+ (DWORD)msg->args[2],
+ (DWORD)msg->args[3]);
+ break;
+
+ case APHP_AddScriptlet:
+ msg->ret = msg->engine->AddScriptlet(
+ (LPCOLESTR)msg->args[0],
+ (LPCOLESTR)msg->args[1],
+ (LPCOLESTR)msg->args[2],
+ (LPCOLESTR)msg->args[3],
+ (LPCOLESTR)msg->args[4],
+ (LPCOLESTR)msg->args[5],
+ (DWORD)msg->args[6],
+ (ULONG)msg->args[7],
+ (DWORD)msg->args[8],
+ (BSTR*)msg->args[9],
+ (EXCEPINFO*)msg->args[10]);
+ break;
+
+ default:
+ msg->ret = E_NOTIMPL;
+ }
+
+ if (SUCCEEDED(msg->ret)) {
+ msg->ret = do_marshal_in(1, msg->args, msg->marshal_defs, &msg->outstm);
+ }
+
+ SetEvent(msg->evt);
+
+ return msg->ret;
+}
+
diff --git a/sapi/activescript/php5activescript.c b/sapi/activescript/php5activescript.c
index 93222fdbf8..6e83b99d5c 100644
--- a/sapi/activescript/php5activescript.c
+++ b/sapi/activescript/php5activescript.c
@@ -145,6 +145,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
activescript_sapi_module.shutdown(&sapi_module);
}
//OutputDebugString("PROCESS_DETACH\n");
+ sapi_shutdown();
tsrm_shutdown();
break;
}
diff --git a/sapi/activescript/php5as_classfactory.h b/sapi/activescript/php5as_classfactory.h
index 61823fb689..048d38b7e5 100644
--- a/sapi/activescript/php5as_classfactory.h
+++ b/sapi/activescript/php5as_classfactory.h
@@ -25,6 +25,10 @@
DEFINE_GUID(CLSID_PHPActiveScriptEngine,
0xcf108a38, 0x59a9, 0x468a, 0xaf, 0x45, 0x13, 0x68, 0xd7, 0x85, 0x5d, 0xae);
+// {AD504760-D6B9-4537-AEAC-512FFB359009}
+DEFINE_GUID(CLSID_PHPActiveScriptEngineMarshal,
+0xad504760, 0xd6b9, 0x4537, 0xae, 0xac, 0x51, 0x2f, 0xfb, 0x35, 0x90, 0x9);
+
#if 0
/* this was for PHP 4 */
// {A0AD8E7A-95EC-4819-986F-78D93895F2AE}
diff --git a/sapi/activescript/php5as_scriptengine.h b/sapi/activescript/php5as_scriptengine.h
index bd720a584c..83ab582cc2 100644
--- a/sapi/activescript/php5as_scriptengine.h
+++ b/sapi/activescript/php5as_scriptengine.h
@@ -18,129 +18,68 @@
/* $Id$ */
#include <activscp.h>
-#if ACTIVEPHP_OBJECT_SAFETY
-# include <objsafe.h>
-#endif
+#include <objsafe.h>
#include "zend.h"
-#include <setjmp.h>
-
-/* Definitions for thread messages */
-enum {
- PHPSE_STATE_CHANGE = WM_USER + 20,
- PHPSE_INIT_NEW,
- PHPSE_PARSE_SCRIPT,
- PHPSE_ADD_SCRIPTLET,
- PHPSE_CLOSE,
- PHPSE_CLONE,
- PHPSE_ENTER,
- PHPSE_LEAVE,
- PHPSE_TERMINATE,
- PHPSE_PARSE_PROC,
- PHPSE_EXEC_PROC,
- PHPSE_ADD_NAMED_ITEM,
- PHPSE_SET_SITE,
- PHPSE_ADD_TYPELIB,
- PHPSE_TRIGGER_ERROR,
- PHPSE_GET_DISPATCH,
- PHPSE_DUMMY_TICK,
-};
-struct php_active_script_get_dispatch_info {
- LPCOLESTR pstrItemName;
- DWORD dispatch;
-};
-
-struct php_active_script_add_named_item_info {
- LPCOLESTR pstrName;
- DWORD dwFlags;
- IUnknown *punk;
- ITypeInfo *ptyp;
- IDispatch *pdisp;
- DWORD marshal;
-};
+#if 0
+#define ACTIVEPHP_THREADING_MODE COINIT_MULTITHREADED
+#else
+#define ACTIVEPHP_THREADING_MODE COINIT_APARTMENTTHREADED
+#endif
-struct php_active_script_add_scriptlet_info {
- /* [in] */ LPCOLESTR pstrDefaultName;
- /* [in] */ LPCOLESTR pstrCode;
- /* [in] */ LPCOLESTR pstrItemName;
- /* [in] */ LPCOLESTR pstrSubItemName;
- /* [in] */ LPCOLESTR pstrEventName;
- /* [in] */ LPCOLESTR pstrDelimiter;
- /* [in] */ DWORD dwSourceContextCookie;
- /* [in] */ ULONG ulStartingLineNumber;
- /* [in] */ DWORD dwFlags;
- /* [out] */ BSTR *pbstrName;
- /* [out] */ EXCEPINFO *pexcepinfo;
-};
+#define ACTIVEPHP_HAS_OWN_THREAD 1
-struct php_active_script_parse_info {
- /* [in] */ LPCOLESTR pstrCode;
- /* [in] */ LPCOLESTR pstrItemName;
- /* [in] */ IUnknown *punkContext;
- /* [in] */ LPCOLESTR pstrDelimiter;
- /* [in] */ DWORD dwSourceContextCookie;
- /* [in] */ ULONG ulStartingLineNumber;
- /* [in] */ DWORD dwFlags;
- /* [out] */ VARIANT *pvarResult;
- /* [out] */ EXCEPINFO *pexcepinfo;
-};
+#define WM_ACTIVEPHP_SERIALIZE WM_USER + 200
-struct php_active_script_parse_proc_info {
- /* [in] */ LPCOLESTR pstrCode;
- /* [in] */ LPCOLESTR pstrFormalParams;
- /* [in] */ LPCOLESTR pstrProcedureName;
- /* [in] */ LPCOLESTR pstrItemName;
- /* [in] */ IUnknown *punkContext;
- /* [in] */ LPCOLESTR pstrDelimiter;
- /* [in] */ DWORD dwSourceContextCookie;
- /* [in] */ ULONG ulStartingLineNumber;
- /* [in] */ DWORD dwFlags;
- DWORD dispcookie;
+enum activephp_engine_func { /* if you change the order, change marshal.cpp too */
+ APHP_ParseScriptText,
+ APHP_InitNew,
+ APHP_AddNamedItem,
+ APHP_SetScriptState,
+ APHP_GetScriptDispatch,
+ APHP_Close,
+ APHP_AddTypeLib,
+ APHP_AddScriptlet,
+ APHP__Max
};
-struct php_active_script_add_tlb_info {
- /* [in] */ const GUID * rguidTypeLib;
- /* [in] */ DWORD dwMajor;
- /* [in] */ DWORD dwMinor;
- /* [in] */ DWORD dwFlags;
-};
+HRESULT marshal_call(class TPHPScriptingEngine *engine, enum activephp_engine_func func, int nargs, ...);
+HRESULT marshal_stub(LPARAM lparam);
class TPHPScriptingEngine:
public IActiveScript,
public IActiveScriptParse,
- public IActiveScriptParseProcedure
-#if ACTIVEPHP_OBJECT_SAFETY
- , public IObjectSafety
+ public IActiveScriptParseProcedure,
+ public IObjectSafety,
+ public IDispatch
+#if 0
+ , public IMarshal
#endif
{
public:
volatile LONG m_refcount;
IActiveScriptSite *m_pass;
SCRIPTSTATE m_scriptstate;
- MUTEX_T m_mutex;
- HashTable m_script_dispatchers;
- HANDLE m_engine_thread_handle;
- HANDLE m_sync_thread_msg;
- HRESULT m_sync_thread_ret;
-
- /* This is hacky, but only used when the host queries us for a script dispatch */
- void *** m_tsrm_hack;
-
void add_to_global_namespace(IDispatch *disp, DWORD flags, char *name TSRMLS_DC);
THREAD_T m_enginethread, m_basethread;
HashTable m_frags;
ULONG m_lambda_count;
- IActiveScriptSite *m_pass_eng;
+ DWORD m_gitcookie, m_asscookie;
+ HWND m_queue;
+
+ int m_done_init;
- jmp_buf *m_err_trap;
int m_in_main, m_stop_main;
-
- HRESULT SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam);
- void engine_thread_func(void);
- HRESULT engine_thread_handler(LONG msg, WPARAM wParam, LPARAM lParam, int *handled TSRMLS_DC);
+ void do_clone(TPHPScriptingEngine *src);
+void setup_engine_state(void);
+ int create_id(OLECHAR *name, DISPID *dispid TSRMLS_DC);
+
+ char *m_names[1024];
+ int m_lens[1024];
+ int m_ids;
public: /* IUnknown */
STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject);
@@ -234,7 +173,6 @@ public: /* IActiveScriptParseProcedure */
/* [in] */ DWORD dwFlags,
/* [out] */ IDispatch **ppdisp);
-#if ACTIVEPHP_OBJECT_SAFETY
public: /* IObjectSafety */
STDMETHODIMP GetInterfaceSafetyOptions(
/* [in] */ REFIID riid, // Interface that we want options for
@@ -245,11 +183,33 @@ public: /* IObjectSafety */
/* [in] */ REFIID riid, // Interface to set options for
/* [in] */ DWORD dwOptionSetMask, // Options to change
/* [in] */ DWORD dwEnabledOptions); // New option values
+#if 0
+public: /* IMarshal */
+ STDMETHODIMP GetUnmarshalClass(
+ REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid);
+ STDMETHODIMP GetMarshalSizeMax(
+ REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, ULONG *pSize);
+ STDMETHODIMP MarshalInterface(
+ IStream *pStm, REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshflags);
+ STDMETHODIMP UnmarshalInterface(
+ IStream *pStm, REFIID riid, void **ppv);
+ STDMETHODIMP ReleaseMarshalData(IStream *pStm);
+ STDMETHODIMP DisconnectObject(DWORD dwReserved);
#endif
+
+public: /* IDispatch */
+ STDMETHODIMP GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID *rgDispId);
+ STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS FAR* pdp, VARIANT FAR* pvarRes, EXCEPINFO FAR* pei,
+ unsigned int FAR* puArgErr);
+ STDMETHODIMP GetTypeInfoCount(unsigned int * pctinfo);
+ STDMETHODIMP GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo);
public:
TPHPScriptingEngine();
~TPHPScriptingEngine();
-
+
};
+IUnknown *create_scripting_engine(TPHPScriptingEngine *tobecloned);
+
diff --git a/sapi/activescript/scriptengine.cpp b/sapi/activescript/scriptengine.cpp
index 9906ca1490..56e5c797c5 100644
--- a/sapi/activescript/scriptengine.cpp
+++ b/sapi/activescript/scriptengine.cpp
@@ -45,6 +45,7 @@ extern "C" {
#include "php5activescript.h"
#include "ext/com_dotnet/php_com_dotnet.h"
#include "ext/com_dotnet/php_com_dotnet_internal.h"
+#include "zend_exceptions.h"
}
#include "php_ticks.h"
#include "php5as_scriptengine.h"
@@ -52,7 +53,30 @@ extern "C" {
#include <objbase.h>
#undef php_win_err
-#define ACTIVEPHP_THREADING_MODE COINIT_MULTITHREADED
+static int clone_frags(void *pDest, void *arg TSRMLS_DC);
+
+#define ENGINE_THREAD_ONLY(type, method) \
+ if (tsrm_thread_id() != m_enginethread) { \
+ trace("WRONG THREAD !! " #type "::" #method "\n"); \
+ return RPC_E_WRONG_THREAD; \
+ } \
+ trace("[direct] " #type "::" #method "\n");
+
+
+#define ASS_CALL(ret, method, args) \
+ if (tsrm_thread_id() == m_basethread) { \
+ trace("Calling [direct] m_pass->" #method "\n"); \
+ ret = m_pass->method args; \
+ } else { \
+ IActiveScriptSite *ass; \
+ trace("Calling [marshall] m_pass->" #method "\n"); \
+ ret = GIT_get(m_asscookie, IID_IActiveScriptSite, (void**)&ass); \
+ if (SUCCEEDED(ret)) { \
+ ret = ass->method args; \
+ ass->Release(); \
+ } \
+ } \
+ trace("--- done calling m_pass->" #method "\n");
/* {{{ trace */
static inline void trace(char *fmt, ...)
@@ -99,10 +123,12 @@ class TWideString {
TWideString(LPOLESTR olestr) {
m_ole = olestr;
m_ansi = NULL;
+ m_ansi_strlen = 0;
}
TWideString(LPCOLESTR olestr) {
m_ole = (LPOLESTR)olestr;
m_ansi = NULL;
+ m_ansi_strlen = 0;
}
~TWideString() {
@@ -110,6 +136,7 @@ class TWideString {
CoTaskMemFree(m_ansi);
}
m_ansi = NULL;
+ m_ansi_strlen = 0;
}
char *safe_ansi_string() {
@@ -162,12 +189,13 @@ class TWideString {
if (m_ansi_strlen) {
m_ansi_strlen--;
- m_ansi[m_ansi_strlen] = 0;
-
} else {
trace("conversion failed with return code %08x\n", GetLastError());
}
}
+ if (m_ansi) {
+ m_ansi[m_ansi_strlen] = 0;
+ }
}
return m_ansi;
}
@@ -213,16 +241,6 @@ static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engi
/* }}} */
-/* Magic for handling threading correctly */
-static inline HRESULT SEND_THREAD_MESSAGE(TPHPScriptingEngine *engine, LONG msg, WPARAM wparam, LPARAM lparam TSRMLS_DC)
-{
- if (engine->m_enginethread == 0)
- return E_UNEXPECTED;
- if (tsrm_thread_id() == (engine)->m_enginethread)
- return (engine)->engine_thread_handler((msg), (wparam), (lparam), NULL TSRMLS_CC);
- return (engine)->SendThreadMessage((msg), (wparam), (lparam));
-}
-
/* These functions do some magic so that interfaces can be
* used across threads without worrying about marshalling
* or not marshalling, as appropriate.
@@ -353,19 +371,183 @@ public:
}
};
/* }}} */
+ STDMETHODIMP TPHPScriptingEngine::GetTypeInfoCount(unsigned int * pctinfo) {
+ *pctinfo = 0;
+ trace("%08x: ScriptDispatch: GetTypeInfoCount\n", this);
+ return S_OK;
+ }
+ STDMETHODIMP TPHPScriptingEngine::GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
+ trace("%08x: ScriptDispatch: GetTypeInfo\n", this);
+ return DISP_E_BADINDEX;
+ }
-/* {{{ This object represents the PHP engine to the scripting host.
- * Although the docs say it's implementation is optional, I found that
- * the Windows Script host would crash if we did not provide it. */
-class ScriptDispatch:
- public IDispatchImpl
+int TPHPScriptingEngine::create_id(OLECHAR *name, DISPID *dispid TSRMLS_DC)
{
-public:
- ScriptDispatch() {
- m_refcount = 1;
+ int ex = 0;
+ char *lcname;
+ int l;
+
+ if (m_ids >= 1023) {
+ trace("too many ids\n");
+ return 0;
}
-};
-/* }}} */
+
+ TWideString aname(name);
+
+ l = aname.ansi_len();
+ lcname = zend_str_tolower_dup(aname.ansi_string(), l);
+ ex = zend_hash_exists(EG(function_table), lcname, l+1);
+ efree(lcname);
+
+ if (!ex) {
+ trace("no such id %s\n", aname.ansi_string());
+ return 0;
+ }
+
+ /* do we already have an id for this name? */
+ int i;
+ for (i = 0; i < m_ids; i++) {
+ if (!strcasecmp(m_names[i], aname.ansi_string())) {
+ trace("already had this ID\n");
+ return i;
+ }
+ }
+
+ m_lens[m_ids] = aname.ansi_len();
+ m_names[m_ids] = aname.ansi_string();
+ trace("created ID %d for name %s\n", m_ids, m_names[m_ids]);
+ aname.m_ansi = NULL;
+ *dispid = m_ids;
+ m_ids++;
+ return 1;
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID *rgDispId)
+{
+ unsigned int i;
+ HRESULT ret = S_OK;
+ TSRMLS_FETCH();
+
+ if (tsrm_thread_id() != m_enginethread) {
+ trace("GetIDsOfNames called from wrong thread\n");
+ return RPC_E_WRONG_THREAD;
+ }
+
+ trace("%08x: ScriptDispatch: GetIDsOfNames %d names: \n", this, cNames);
+ for (i = 0; i < cNames; i++) {
+ if (!create_id(rgszNames[i], &rgDispId[i] TSRMLS_CC)) {
+ ret = DISP_E_UNKNOWNNAME;
+ break;
+ }
+ }
+ return ret;
+}
+
+STDMETHODIMP TPHPScriptingEngine::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS FAR* pdp, VARIANT FAR* pvarRes, EXCEPINFO FAR* pei,
+ unsigned int FAR* puArgErr)
+{
+ UINT i;
+ zval *retval = NULL;
+ zval ***params = NULL;
+ HRESULT ret = DISP_E_MEMBERNOTFOUND;
+ char *name;
+ int namelen;
+ TSRMLS_FETCH();
+
+ if (tsrm_thread_id() != m_enginethread) {
+ trace("Invoke called from wrong thread\n");
+ return RPC_E_WRONG_THREAD;
+ }
+
+ name = m_names[dispIdMember];
+ namelen = m_lens[dispIdMember];
+
+ trace("%08x: ScriptDispatch: Invoke dispid %08x [%s]\n", this, dispIdMember, name);
+ /* this code is similar to that of our com_wrapper InvokeEx implementation */
+
+ /* convert args into zvals.
+ * Args are in reverse order */
+ if (pdp->cArgs) {
+ params = (zval ***)safe_emalloc(sizeof(zval **), pdp->cArgs, 0);
+ for (i = 0; i < pdp->cArgs; i++) {
+ VARIANT *arg;
+ zval *zarg;
+
+ arg = &pdp->rgvarg[ pdp->cArgs - 1 - i];
+
+ trace("alloc zval for arg %d VT=%08x\n", i, V_VT(arg));
+
+ ALLOC_INIT_ZVAL(zarg);
+ php_com_wrap_variant(zarg, arg, CP_ACP TSRMLS_CC);
+ params[i] = &zarg;
+ }
+ }
+
+ trace("arguments processed, prepare to do some work\n");
+
+ /* TODO: if PHP raises an exception here, we should catch it
+ * and expose it as a COM exception */
+
+#if 0
+ if (wFlags & DISPATCH_PROPERTYGET) {
+ retval = zend_read_property(Z_OBJCE_P(disp->object), disp->object, m_names[dispIdMember], m_lens[dispIdMember]+1, 1 TSRMLS_CC);
+ } else if (wFlags & DISPATCH_PROPERTYPUT) {
+ zend_update_property(Z_OBJCE_P(disp->object), disp->object, m_names[dispIdMember], m_lens[dispIdMember]+1, *params[0] TSRMLS_CC);
+ } else
+#endif
+ if (wFlags & DISPATCH_METHOD) {
+ zval *zname;
+ MAKE_STD_ZVAL(zname);
+ ZVAL_STRINGL(zname, (char*)name, namelen, 1);
+ trace("invoke function %s\n", Z_STRVAL_P(zname));
+
+ zend_try {
+
+ if (SUCCESS == call_user_function_ex(CG(function_table), NULL, zname,
+ &retval, pdp->cArgs, params, 1, NULL TSRMLS_CC)) {
+ ret = S_OK;
+ trace("we ran it\n");
+ } else {
+ ret = DISP_E_EXCEPTION;
+ trace("no such function\n");
+ }
+
+ } zend_catch {
+ ret = DISP_E_EXCEPTION;
+ /* need to populate the exception here */
+ trace("bork\n");
+ } zend_end_try();
+
+ zval_ptr_dtor(&zname);
+ } else {
+ trace("Don't know how to handle this invocation %08x\n", wFlags);
+ ret = E_UNEXPECTED;
+ }
+
+ /* release arguments */
+ for (i = 0; i < pdp->cArgs; i++)
+ zval_ptr_dtor(params[i]);
+
+ if (params)
+ efree(params);
+
+ /* return value */
+ if (retval) {
+ if (pvarRes) {
+ VariantInit(pvarRes);
+ trace("setting up return value\n");
+ php_com_variant_from_zval(pvarRes, retval, CP_ACP TSRMLS_CC);
+ }
+ zval_ptr_dtor(&retval);
+ } else if (pvarRes) {
+ VariantInit(pvarRes);
+ }
+
+ trace("Invocation complete\n");
+
+ return ret;
+}
/* {{{ This object is used in conjunction with IActiveScriptParseProcedure to
* allow scriptlets to be bound to events. IE uses this for declaring
@@ -390,7 +572,7 @@ public:
if (m_frag) {
trace("%08x: Procedure Dispatch: Invoke dispid %08x\n", this, dispIdMember);
- SEND_THREAD_MESSAGE(m_engine, PHPSE_EXEC_PROC, 0, (LPARAM)this TSRMLS_CC);
+ execute_code_fragment(m_frag, NULL, NULL TSRMLS_CC);
}
return S_OK;
}
@@ -496,10 +678,16 @@ static void free_code_fragment(code_frag *frag TSRMLS_DC)
break;
}
- if (frag->opcodes)
+ if (frag->opcodes) {
destroy_op_array(frag->opcodes TSRMLS_CC);
- if (frag->functionname)
+ frag->opcodes = NULL;
+ }
+
+ if (frag->functionname) {
CoTaskMemFree(frag->functionname);
+ frag->functionname = NULL;
+ }
+
CoTaskMemFree(frag->code);
CoTaskMemFree(frag);
}
@@ -533,23 +721,12 @@ trace("%08x: CLONED FRAG\n", newfrag);
pv.value.str.val = newfrag->code;
pv.value.str.len = newfrag->codelen;
- newfrag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC);
+ /* we defer compilation until we are ready to execute,
+ * as we need the host to AddNamedItem certain autoglobals
+ * BEFORE we compile */
+ newfrag->opcodes = NULL;
- if (newfrag->opcodes == NULL) {
- free_code_fragment(newfrag TSRMLS_CC);
-/*
- if (excepinfo) {
- memset(excepinfo, 0, sizeof(EXCEPINFO));
- excepinfo->wCode = 1000;
- excepinfo->bstrSource = TWideString::bstr_from_ansi("fragment");
- excepinfo->bstrDescription = TWideString::bstr_from_ansi("Problem while parsing/compiling");
- }
-*/
- return NULL;
- }
-
return newfrag;
-
}
static int execute_code_fragment(code_frag *frag,
@@ -558,16 +735,26 @@ static int execute_code_fragment(code_frag *frag,
TSRMLS_DC)
{
zval *retval_ptr = NULL;
- jmp_buf *orig_jmpbuf;
- jmp_buf err_trap;
if (frag->fragtype == FRAG_MAIN && frag->executed)
return 1;
-
- orig_jmpbuf = frag->engine->m_err_trap;
- frag->engine->m_err_trap = &err_trap;
- if (setjmp(err_trap) == 0) {
+ /* compiled cloned fragment, JIT */
+ if (frag->persistent && frag->opcodes == NULL) {
+ zval pv;
+ pv.type = IS_STRING;
+ pv.value.str.val = frag->code;
+ pv.value.str.len = frag->codelen;
+
+ frag->opcodes = compile_string(&pv, "fragment (JIT)" TSRMLS_CC);
+
+ if (!frag->opcodes) {
+ trace("*** JIT compilation of cloned opcodes failed??");
+ return 0;
+ }
+ }
+
+ zend_try {
trace("*** Executing code in thread %08x\n", tsrm_thread_id());
if (frag->functionname) {
@@ -581,30 +768,29 @@ static int execute_code_fragment(code_frag *frag,
call_user_function_ex(CG(function_table), NULL, &fname, &retval_ptr, 0, NULL, 1, NULL TSRMLS_CC);
} else {
- zend_op_array *active_op_array = EG(active_op_array);
- zend_function_state *function_state_ptr = EG(function_state_ptr);
- zval **return_value_ptr_ptr = EG(return_value_ptr_ptr);
- zend_op **opline_ptr = EG(opline_ptr);
-
- EG(return_value_ptr_ptr) = &retval_ptr;
- EG(active_op_array) = frag->opcodes;
- EG(no_extensions) = 1;
-
- zend_execute(frag->opcodes TSRMLS_CC);
-
- EG(no_extensions) = 0;
- EG(opline_ptr) = opline_ptr;
- EG(active_op_array) = active_op_array;
- EG(function_state_ptr) = function_state_ptr;
- EG(return_value_ptr_ptr) = return_value_ptr_ptr;
+ zend_fcall_info_cache fci_cache;
+ zend_fcall_info fci;
+
+ memset(&fci, 0, sizeof(fci));
+ memset(&fci_cache, 0, sizeof(fci_cache));
+
+ fci.size = sizeof(fci);
+ fci.function_table = CG(function_table);
+ fci.retval_ptr_ptr = &retval_ptr;
+ fci.no_separation = 1;
+
+ fci_cache.initialized = 1;
+ fci_cache.function_handler = (zend_function*)frag->opcodes;
+ frag->opcodes->type = ZEND_USER_FUNCTION; // mini hack
+
+ zend_call_function(&fci, &fci_cache TSRMLS_CC);
+
}
- } else {
+ } zend_catch {
trace("*** --> caught error while executing\n");
if (frag->engine->m_in_main)
frag->engine->m_stop_main = 1;
- }
-
- frag->engine->m_err_trap = orig_jmpbuf;
+ } zend_end_try();
if (frag->fragtype == FRAG_MAIN)
frag->executed = 1;
@@ -628,124 +814,189 @@ static void frag_dtor(void *pDest)
free_code_fragment(frag TSRMLS_CC);
}
/* }}} */
-
-/* glue for getting back into the OO land */
-static DWORD WINAPI begin_engine_thread(LPVOID param)
+
+void TPHPScriptingEngine::do_clone(TPHPScriptingEngine *src)
{
- TPHPScriptingEngine *engine = (TPHPScriptingEngine*)param;
- engine->engine_thread_func();
- trace("engine thread has really gone away!\n");
- return 0;
+ TSRMLS_FETCH();
+ zend_hash_apply_with_argument(&src->m_frags, clone_frags, this TSRMLS_CC);
}
-TPHPScriptingEngine::TPHPScriptingEngine()
+#if ACTIVEPHP_HAS_OWN_THREAD
+struct engine_startup {
+ HANDLE evt;
+ HRESULT ret;
+ TPHPScriptingEngine *toclone;
+ TPHPScriptingEngine *localref;
+};
+
+static WNDCLASS wc = {0};
+
+static LRESULT CALLBACK script_thread_msg_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
- m_scriptstate = SCRIPTSTATE_UNINITIALIZED;
- m_pass = NULL;
- m_in_main = 0;
- m_stop_main = 0;
- m_err_trap = NULL;
- m_lambda_count = 0;
- m_pass_eng = NULL;
- m_refcount = 1;
- m_basethread = tsrm_thread_id();
- m_mutex = tsrm_mutex_alloc();
- m_sync_thread_msg = CreateEvent(NULL, TRUE, FALSE, NULL);
- TPHPClassFactory::AddToObjectCount();
-
- m_engine_thread_handle = CreateThread(NULL, 0, begin_engine_thread, this, 0, &m_enginethread);
+ switch (message) {
+ case WM_ACTIVEPHP_SERIALIZE:
+ return marshal_stub(lparam);
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+
+ default:
+ return DefWindowProc(hwnd, message, wparam, lparam);
+ }
}
-void activescript_run_ticks(int count)
+static DWORD WINAPI script_thread(LPVOID param)
{
- MSG msg;
- TSRMLS_FETCH();
+ struct engine_startup *su = (struct engine_startup*)param;
TPHPScriptingEngine *engine;
+ IUnknown *punk = NULL;
+ MSG msg;
+
+ trace("firing up engine thread/apartment\n");
- trace("ticking %d\n", count);
+ /* set up COM in this apartment */
+ CoInitializeEx(0, COINIT_APARTMENTTHREADED);
+
+ /* create a window for message queueing */
+ wc.lpfnWndProc = script_thread_msg_proc;
+ wc.lpszClassName = "ActivePHP Message Window";
+ RegisterClass(&wc);
- engine = (TPHPScriptingEngine*)SG(server_context);
+ /* create the engine state */
+ engine = new TPHPScriptingEngine;
+
+ engine->m_enginethread = tsrm_thread_id();
+ engine->m_basethread = 0;
+ engine->m_queue = CreateWindow(wc.lpszClassName, wc.lpszClassName,
+ 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, NULL, NULL);
+
+ /* marshall it for another apartment */
+ engine->QueryInterface(IID_IUnknown, (void**)&punk);
+ su->ret = GIT_put(punk, IID_IUnknown, &engine->m_gitcookie);
+ punk->Release();
+ su->localref = engine;
+
+ /* do we need to clone ? */
+ if (su->toclone) {
+ engine->do_clone(su->toclone);
+ }
+
+ /* tell whoever spawned us that we're ready and waiting */
+ SetEvent(su->evt);
+
+ /* pump COM messages */
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
-/* PostThreadMessage(engine->m_enginethread, PHPSE_DUMMY_TICK, 0, 0); */
+ trace("terminating engine thread\n");
- while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
- if (msg.hwnd) {
- PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- } else {
- break;
- }
+ CoUninitialize();
+
+ return 0;
+}
+#endif
+
+IUnknown *create_scripting_engine(TPHPScriptingEngine *tobecloned)
+{
+ IUnknown *punk = NULL;
+#if ACTIVEPHP_HAS_OWN_THREAD
+ struct engine_startup su;
+ HANDLE hthr;
+ DWORD thid;
+
+ su.evt = CreateEvent(NULL, TRUE, FALSE, NULL);
+ su.toclone = tobecloned;
+
+ hthr = CreateThread(NULL, 0, script_thread, &su, 0, &thid);
+ if (hthr)
+ CloseHandle(hthr);
+
+ WaitForSingleObject(su.evt, INFINITE);
+
+ if (SUCCEEDED(su.ret)) {
+ punk = (IUnknown*)(void*)su.localref;
}
+
+ CloseHandle(su.evt);
+#else
+ punk = (IActiveScript*)new TPHPScriptingEngine;
+#endif
+ return punk;
}
-/* Synchronize with the engine thread */
-HRESULT TPHPScriptingEngine::SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam)
+void TPHPScriptingEngine::setup_engine_state(void)
{
- HRESULT ret;
+ TSRMLS_FETCH();
- if (m_enginethread == 0)
- return E_UNEXPECTED;
+ m_enginethread = tsrm_thread_id();
+ trace("initializing zend engine on this thread\n");
+
+ SG(options) |= SAPI_OPTION_NO_CHDIR;
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
- trace("I'm waiting for a mutex in SendThreadMessage\n this=%08x ethread=%08x msg=%08x\n",
- this, m_enginethread, msg);
+ SG(server_context) = this;
+
+ /* override the default PHP error callback */
+ zend_error_cb = activescript_error_handler;
+
+ zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+ zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+ zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+ zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+
+ php_request_startup(TSRMLS_C);
+ PG(during_request_startup) = 0;
+ zend_hash_init(&m_frags, 0, NULL, frag_dtor, 0);
+
+ m_done_init = 1;
- tsrm_mutex_lock(m_mutex);
- ResetEvent(m_sync_thread_msg);
-
- /* If we call PostThreadMessage before the thread has created the queue, the message
- * posting fails. MSDN docs recommend the following course of action */
- while (!PostThreadMessage(m_enginethread, msg, wparam, lparam)) {
- Sleep(50);
- if (m_enginethread == 0) {
- tsrm_mutex_unlock(m_mutex);
- trace("breaking out of dodgy busy wait\n");
- return E_UNEXPECTED;
- }
- }
+ trace("---- init done\n");
+}
- /* Wait for the event object to be signalled.
- * This is a nice "blocking without blocking" wait; window messages are dispatched
- * and everything works out quite nicely */
- while(1) {
- DWORD result = MsgWaitForMultipleObjects(1, &m_sync_thread_msg, FALSE, 4000, QS_ALLINPUT);
+TPHPScriptingEngine::TPHPScriptingEngine()
+{
+ TSRMLS_FETCH();
+
+ /* CTOR */
- if (result == WAIT_OBJECT_0 + 1) {
- /* Dispatch some messages */
- MSG msg;
- while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
- //trace("dispatching message while waiting\n");
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- } else if (result == WAIT_TIMEOUT) {
- trace("timeout while waiting for thread reply\n");
+ trace("*** NEW this=%08x\n", this);
+
+ m_scriptstate = SCRIPTSTATE_UNINITIALIZED;
+ m_pass = NULL;
+ m_in_main = 0;
+ m_done_init = 0;
+ m_stop_main = 0;
+ m_lambda_count = 0;
+ m_refcount = 1;
+ m_ids = 0;
+ TPHPClassFactory::AddToObjectCount();
+
+#if ACTIVEPHP_HAS_OWN_THREAD
+ setup_engine_state();
+#endif
- } else {
- /* the event was signalled */
- break;
- }
- }
- ret = m_sync_thread_ret;
- ResetEvent(m_sync_thread_msg);
- tsrm_mutex_unlock(m_mutex);
- return ret;
}
TPHPScriptingEngine::~TPHPScriptingEngine()
{
+ /* DTOR */
+
trace("\n\n *** Engine Destructor Called\n\n");
- if (m_scriptstate != SCRIPTSTATE_UNINITIALIZED && m_scriptstate != SCRIPTSTATE_CLOSED && m_enginethread) {
+ if (m_done_init && m_scriptstate != SCRIPTSTATE_CLOSED) {
Close();
}
-
- PostThreadMessage(m_enginethread, WM_QUIT, 0, 0);
- WaitForSingleObject(m_engine_thread_handle, INFINITE);
- CloseHandle(m_engine_thread_handle);
+
+ while (m_ids--) {
+ CoTaskMemFree(m_names[m_ids]);
+ }
TPHPClassFactory::RemoveFromObjectCount();
- tsrm_mutex_free(m_mutex);
}
/* Set some executor globals and execute a zend_op_array.
@@ -777,545 +1028,17 @@ static int clone_frags(void *pDest, void *arg TSRMLS_DC)
return ZEND_HASH_APPLY_KEEP;
}
-HRESULT TPHPScriptingEngine::engine_thread_handler(LONG msg, WPARAM wparam, LPARAM lParam, int *handled TSRMLS_DC)
-{
- HRESULT ret = S_OK;
-
- trace("engine_thread_handler: running in thread %08x, should be %08x msg=%08x this=%08x\n",
- tsrm_thread_id(), m_enginethread, msg, this);
-
- if (handled)
- *handled = 1;
-
- if (m_enginethread == 0)
- return E_UNEXPECTED;
-
- switch(msg) {
- case PHPSE_ADD_TYPELIB:
- {
- struct php_active_script_add_tlb_info *info = (struct php_active_script_add_tlb_info*)lParam;
- ITypeLib *TypeLib;
-
- if (SUCCEEDED(LoadRegTypeLib(*info->rguidTypeLib, (USHORT)info->dwMajor,
- (USHORT)info->dwMinor, LANG_NEUTRAL, &TypeLib))) {
- php_com_import_typelib(TypeLib, CONST_CS, CP_ACP TSRMLS_CC);
- TypeLib->Release();
- }
- }
- break;
- case PHPSE_STATE_CHANGE:
- {
- /* handle the state change here */
- SCRIPTSTATE ss = (SCRIPTSTATE)lParam;
- int start_running = 0;
- trace("%08x: DoSetScriptState(current=%s, new=%s)\n",
- this,
- scriptstate_to_string(m_scriptstate),
- scriptstate_to_string(ss));
-
- if (m_scriptstate == SCRIPTSTATE_INITIALIZED && (ss == SCRIPTSTATE_STARTED || ss == SCRIPTSTATE_CONNECTED))
- start_running = 1;
-
- m_scriptstate = ss;
-
- /* inform host/site of the change */
- if (m_pass_eng)
- m_pass_eng->OnStateChange(m_scriptstate);
-
- if (start_running) {
- /* run "main()", as described in the docs */
- if (m_pass_eng)
- m_pass_eng->OnEnterScript();
- trace("%08x: apply execute main to m_frags\n", this);
- m_in_main = 1;
- m_stop_main = 0;
- zend_hash_apply_with_argument(&m_frags, execute_main, this TSRMLS_CC);
- m_in_main = 0;
- trace("%08x: --- done execute main\n", this);
- if (m_pass_eng)
- m_pass_eng->OnLeaveScript();
-
- /* docs are a bit ambiguous here, but it appears that we should
- * inform the host that the main script execution has completed,
- * and also what the return value is */
- VARIANT varRes;
-
- VariantInit(&varRes);
- if (m_pass_eng)
- m_pass_eng->OnScriptTerminate(&varRes, NULL);
-
- /*
- m_scriptstate = SCRIPTSTATE_INITIALIZED;
- if (m_pass_eng)
- m_pass_eng->OnStateChange(m_scriptstate);
- */
- }
- }
- break;
- case PHPSE_INIT_NEW:
- {
- /* Prepare PHP/ZE for use */
-
- trace("%08x: m_frags : INIT NEW\n", this);
-
-#if 0
- zend_hash_init(&m_frags, 0, NULL, frag_dtor, TRUE);
-
- SG(options) |= SAPI_OPTION_NO_CHDIR;
- SG(server_context) = this;
- /* override the default PHP error callback */
- zend_error_cb = activescript_error_handler;
-
- zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
- zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
- zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
- zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-
- php_request_startup(TSRMLS_C);
- PG(during_request_startup) = 0;
-// trace("\n\n *** ticks func at %08x %08x ***\n\n\n", activescript_run_ticks, &activescript_run_ticks);
-// php_add_tick_function(activescript_run_ticks);
-#endif
-
- }
- break;
- case PHPSE_CLOSE:
- {
- /* Close things down */
- trace("%08x: m_frags : CLOSE/DESTROY\n", this);
- m_scriptstate = SCRIPTSTATE_CLOSED;
- if (m_pass_eng) {
- m_pass_eng->OnStateChange(m_scriptstate);
- trace("%08x: release site from this side\n", this);
- m_pass_eng->Release();
- m_pass_eng = NULL;
- }
-#if 0
- zend_hash_destroy(&m_frags);
- php_request_shutdown(NULL);
-#endif
-
- break;
- }
- break;
- case PHPSE_CLONE:
- {
- /* Clone the engine state. This is semantically equal to serializing all
- * the parsed code from the source and unserializing it in the dest (this).
- * IE doesn't appear to use it, but Windows Script Host does. I'd expect
- * ASP/ASP.NET to do so also.
- *
- * FIXME: Probably won't work with IActiveScriptParseProcedure scriplets
- * */
-
- TPHPScriptingEngine *src = (TPHPScriptingEngine*)lParam;
-
- trace("%08x: m_frags : CLONE\n", this);
- zend_hash_apply_with_argument(&src->m_frags, clone_frags, this TSRMLS_CC);
-
- }
- break;
- case PHPSE_ADD_SCRIPTLET:
- {
- /* Parse/compile a chunk of script that will act as an event handler.
- * If the host supports IActiveScriptParseProcedure, this code will
- * not be called.
- * The docs are (typically) vague: AFAICT, once the code has been
- * compiled, we are supposed to arrange for an IConnectionPoint
- * advisory connection to the item/subitem, once the script
- * moves into SCRIPTSTATE_CONNECTED.
- * That's a lot of work!
- *
- * FIXME: this is currently almost useless
- * */
-
- struct php_active_script_add_scriptlet_info *info = (struct php_active_script_add_scriptlet_info*)lParam;
-
- TWideString
- default_name(info->pstrDefaultName),
- code(info->pstrCode),
- item_name(info->pstrItemName),
- sub_item_name(info->pstrSubItemName),
- event_name(info->pstrEventName),
- delimiter(info->pstrDelimiter);
-
- /* lets invent a function name for the scriptlet */
- char sname[256];
-
- /* should check if the name is already used! */
- if (info->pstrDefaultName)
- strcpy(sname, default_name.ansi_string());
- else {
- sname[0] = 0;
- strcat(sname, "__");
- if (info->pstrItemName) {
- strcat(sname, item_name.ansi_string());
- strcat(sname, "_");
- }
- if (info->pstrSubItemName) {
- strcat(sname, sub_item_name.ansi_string());
- strcat(sname, "_");
- }
- if (info->pstrEventName)
- strcat(sname, event_name.ansi_string());
- }
-
-
- 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",
- this, scriptstate_to_string(m_scriptstate),
- default_name.safe_ansi_string(), code.safe_ansi_string(), item_name.safe_ansi_string(),
- sub_item_name.safe_ansi_string(), event_name.safe_ansi_string(), delimiter.safe_ansi_string(),
- info->ulStartingLineNumber);
-
-
- code_frag *frag = compile_code_fragment(
- FRAG_SCRIPTLET,
- sname,
- info->pstrCode,
- info->ulStartingLineNumber,
- info->pexcepinfo,
- this
- TSRMLS_CC);
-
- if (frag) {
-
- frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
-
- zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
-
- /*
- ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
-
- disp->AddRef();
- disp->m_frag = frag;
- disp->m_procflags = info->dwFlags;
- disp->m_engine = this;
- frag->ptr = disp;
-
- *info->ppdisp = disp;
- */
- ret = S_OK;
- } else {
- ret = DISP_E_EXCEPTION;
- }
-
- *info->pbstrName = TWideString::bstr_from_ansi(sname);
-
- trace("%08x: done with scriptlet %s\n", this, sname);
-
- }
- break;
- case PHPSE_GET_DISPATCH:
- {
- struct php_active_script_get_dispatch_info *info = (struct php_active_script_get_dispatch_info *)lParam;
- IDispatch *disp = NULL;
-
- if (info->pstrItemName != NULL) {
- zval **tmp;
- /* use this rather than php_OLECHAR_to_char because we want to avoid emalloc here */
- TWideString itemname(info->pstrItemName);
-
- /* Get that item from the global namespace.
- * If it is an object, export it as a dispatchable object.
- * */
-
- if (zend_hash_find(&EG(symbol_table), itemname.ansi_string(),
- itemname.ansi_len() + 1, (void**)&tmp) == SUCCESS) {
- if (Z_TYPE_PP(tmp) == IS_OBJECT) {
- /* FIXME: if this causes an allocation (emalloc) and we are
- * not in the engine thread, things could get ugly!!! */
- disp = php_com_wrapper_export(*tmp TSRMLS_CC);
- }
- }
-
- } else {
- /* This object represents PHP global namespace */
- disp = (IDispatch*) new ScriptDispatch;
- }
-
- if (disp) {
- ret = GIT_put(disp, IID_IDispatch, &info->dispatch);
- disp->Release();
- trace("GET_DISPATCH: we put it in the GIT\n");
- } else {
- ret = S_FALSE;
- trace("GET_DISPATCH: FAILED to put it in the GIT\n");
- }
- }
- break;
- case PHPSE_ADD_NAMED_ITEM:
- {
- /* The Host uses this to add objects to the global namespace.
- * Some objects are intended to have their child properties
- * globally visible, so we add those to the global namespace too.
- * */
- struct php_active_script_add_named_item_info *info = (struct php_active_script_add_named_item_info *)lParam;
-
- TWideString name(info->pstrName);
- IDispatch *disp = NULL;
- HRESULT res;
-
- trace("ADD_NAMED_ITEM\n");
-
-#if ACTIVEPHP_THREADING_MODE == COINIT_MULTITHREADED
- res = info->punk->QueryInterface(IID_IDispatch, (void**)&disp);
-#else
- res = GIT_get(info->marshal, IID_IDispatch, (void**)&disp);
-#endif
- if (SUCCEEDED(res) && disp) {
- add_to_global_namespace(disp, info->dwFlags, name.ansi_string() TSRMLS_CC);
- disp->Release();
- } else {
- trace("Ouch: failed to get IDispatch for %s from GIT '%s'\n", name.ansi_string(), php_win_err(res));
- }
-
- }
- break;
- case PHPSE_SET_SITE:
- {
- if (m_pass_eng) {
- m_pass_eng->Release();
- m_pass_eng = NULL;
- }
-
- if (lParam)
- GIT_get(lParam, IID_IActiveScriptSite, (void**)&m_pass_eng);
-
- trace("%08x: site (engine-side) is now %08x (base=%08x)\n", this, m_pass_eng, m_pass);
-
- }
- break;
- case PHPSE_EXEC_PROC:
- {
- ScriptProcedureDispatch *disp = (ScriptProcedureDispatch *)lParam;
- execute_code_fragment(disp->m_frag, NULL, NULL TSRMLS_CC);
- }
- break;
- case PHPSE_PARSE_PROC:
- {
- /* This is the IActiveScriptParseProcedure implementation.
- * IE uses this to request for an IDispatch that it will invoke in
- * response to some event, and tells us the code that it wants to
- * run.
- * We compile the code and pass it back a dispatch object.
- * The object will then serialize access to the engine thread and
- * execute the opcodes */
- struct php_active_script_parse_proc_info *info = (struct php_active_script_parse_proc_info*)lParam;
- TWideString
- formal_params(info->pstrFormalParams),
- procedure_name(info->pstrProcedureName),
- item_name(info->pstrItemName),
- delimiter(info->pstrDelimiter);
-
- trace("%08x: ParseProc:\n state=%s\nparams=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
- this, scriptstate_to_string(m_scriptstate),
- formal_params.ansi_string(), procedure_name.ansi_string(),
- item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
- info->ulStartingLineNumber);
-
-
- code_frag *frag = compile_code_fragment(
- FRAG_PROCEDURE,
- NULL,
- info->pstrCode,
- info->ulStartingLineNumber,
- NULL,
- this
- TSRMLS_CC);
-
- if (frag) {
-
- frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
- zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
-
- ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
-
- disp->m_frag = frag;
- disp->m_procflags = info->dwFlags;
- disp->m_engine = this;
- frag->ptr = disp;
- info->dispcookie = disp->m_gitcookie;
-
- } else {
- ret = DISP_E_EXCEPTION;
- }
-
- }
- break;
-
- case PHPSE_PARSE_SCRIPT:
- {
- struct php_active_script_parse_info *info = (struct php_active_script_parse_info*)lParam;
- int doexec;
-
- TWideString
- code(info->pstrCode),
- item_name(info->pstrItemName),
- delimiter(info->pstrDelimiter);
-
- trace("%08x: ParseScriptText:\n state=%s\ncode=%s\n item=%s\n delim=%s\n line=%d\n",
- this, scriptstate_to_string(m_scriptstate),
- code.safe_ansi_string(), item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
- info->ulStartingLineNumber);
-
- code_frag *frag = compile_code_fragment(
- FRAG_MAIN,
- info->dwFlags & SCRIPTTEXT_ISEXPRESSION ? FRAG_CREATE_FUNC : NULL,
- info->pstrCode,
- info->ulStartingLineNumber,
- info->pexcepinfo,
- this
- TSRMLS_CC);
-
- doexec = (info->dwFlags & SCRIPTTEXT_ISEXPRESSION) ||
- m_scriptstate == SCRIPTSTATE_STARTED ||
- m_scriptstate == SCRIPTSTATE_CONNECTED ||
- m_scriptstate == SCRIPTSTATE_DISCONNECTED;
-
- if (frag) {
- frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
- ret = S_OK;
-
- if (info->dwFlags & SCRIPTTEXT_ISEXPRESSION) {
- if (m_scriptstate == SCRIPTSTATE_INITIALIZED) {
- /* not allowed to execute code in this state */
- ret = E_UNEXPECTED;
- doexec = 0;
- }
- }
-
- if (doexec) {
- /* execute the code as an expression */
- if (!execute_code_fragment(frag, info->pvarResult, info->pexcepinfo TSRMLS_CC))
- ret = DISP_E_EXCEPTION;
- }
-
- zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
- } else {
- ret = DISP_E_EXCEPTION;
- }
-
- if (info->pvarResult) {
- VariantInit(info->pvarResult);
- }
-
-
- }
- break;
- default:
- trace("XXXXX: unhandled message type %08x\n", msg);
- if (handled)
- *handled = 0;
- }
- return ret;
-}
-
-/* The PHP/Zend state actually lives in this thread */
-void TPHPScriptingEngine::engine_thread_func(void)
-{
- TSRMLS_FETCH();
- int handled;
- int terminated = 0;
- MSG msg;
-
- trace("%08x: engine thread started up!\n", this);
-
- CoInitializeEx(0, ACTIVEPHP_THREADING_MODE);
-
- zend_first_try {
- m_tsrm_hack = tsrm_ls;
-
- SG(options) |= SAPI_OPTION_NO_CHDIR;
- SG(headers_sent) = 1;
- SG(request_info).no_headers = 1;
-
- SG(server_context) = this;
- /* override the default PHP error callback */
- zend_error_cb = activescript_error_handler;
-
- zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
- zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
- zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
- zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-
- if (SUCCESS == php_request_startup(TSRMLS_C)) {
- PG(during_request_startup) = 0;
- if (FAILURE == zend_hash_init(&m_frags, 0, NULL, frag_dtor, 1)) {
- trace("failed to init frags hash\n");
- }
-
- while(!terminated) {
- DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, 4000, QS_ALLINPUT);
-
- switch(result) {
- case WAIT_OBJECT_0:
- while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
-
- if (msg.message == WM_QUIT) {
- terminated = 1;
- } else if (msg.hwnd) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
-
- } else {
- handled = 1;
- m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
- if (handled)
- SetEvent(m_sync_thread_msg);
- }
-
- }
- break;
- case WAIT_TIMEOUT:
- trace("thread wait timed out\n");
- break;
- default:
- trace("some strange value\n");
- }
- }
-
-#if 0
- while(GetMessage(&msg, NULL, 0, 0)) {
-
- if (msg.message == WM_QUIT)
- break;
-
- handled = 1;
- m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
- if (handled)
- SetEvent(m_sync_thread_msg);
- }
-#endif
-
- trace("%08x: engine thread exiting!!!!!\n", this);
- zend_hash_destroy(&m_frags);
- trace("calling request shutdown\n");
- php_request_shutdown(NULL);
- } else {
- trace("request startup failed\n");
- }
-
- } zend_catch {
- trace("ouch: bailed out\n");
- } zend_end_try();
-
- m_enginethread = 0;
- CoUninitialize();
-}
-
/* Only call this in the context of the engine thread, or you'll be sorry.
*
* When SCRIPTITEM_GLOBALMEMBERS is set, we're only adding COM objects to the namespace.
* We could add *all* properties, but I don't like this idea; what if the value changes
* while the page is running? We'd be left with stale data.
- *
- * TODO: evaluate if it is appropriate to register as an auto_global
* */
static inline void make_auto_global(char *name, zval *val TSRMLS_DC)
{
int namelen = strlen(name);
-
+trace("make_auto_global %s\n", name);
zend_register_auto_global(name, namelen, NULL TSRMLS_CC);
zend_auto_global_disable_jit(name, namelen TSRMLS_CC);
ZEND_SET_SYMBOL(&EG(symbol_table), name, val);
@@ -1423,7 +1146,10 @@ trace("Add %s to global namespace\n", name);
STDMETHODIMP_(DWORD) TPHPScriptingEngine::AddRef(void)
{
- return InterlockedIncrement(const_cast<long*> (&m_refcount));
+ DWORD ret;
+ ret = InterlockedIncrement(const_cast<long*> (&m_refcount));
+ trace("AddRef --> %d\n", ret);
+ return ret;
}
STDMETHODIMP_(DWORD) TPHPScriptingEngine::Release(void)
@@ -1433,25 +1159,26 @@ STDMETHODIMP_(DWORD) TPHPScriptingEngine::Release(void)
trace("%08x: Release: zero refcount, destroy the engine!\n", this);
delete this;
}
+ trace("Release --> %d\n", ret);
return ret;
}
STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
{
*ppvObject = NULL;
-
+
if (IsEqualGUID(IID_IActiveScript, iid)) {
*ppvObject = (IActiveScript*)this;
} else if (IsEqualGUID(IID_IActiveScriptParse, iid)) {
*ppvObject = (IActiveScriptParse*)this;
} else if (IsEqualGUID(IID_IActiveScriptParseProcedure, iid)) {
*ppvObject = (IActiveScriptParseProcedure*)this;
-#if ACTIVEPHP_OBJECT_SAFETY
} else if (IsEqualGUID(IID_IObjectSafety, iid)) {
*ppvObject = (IObjectSafety*)this;
-#endif
} else if (IsEqualGUID(IID_IUnknown, iid)) {
*ppvObject = this;
+ } else if (IsEqualGUID(IID_IDispatch, iid)) {
+ *ppvObject = (IDispatch*)this;
} else {
LPOLESTR guidw;
StringFromCLSID(iid, &guidw);
@@ -1465,7 +1192,7 @@ STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
AddRef();
return S_OK;
}
-
+
return E_NOINTERFACE;
}
@@ -1473,38 +1200,53 @@ STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
* It also defines the base thread. */
STDMETHODIMP TPHPScriptingEngine::SetScriptSite(IActiveScriptSite *pass)
{
+ HRESULT ret = S_OK;
TSRMLS_FETCH();
- tsrm_mutex_lock(m_mutex);
+ if (m_pass && pass) {
+ trace("SetScriptSite: we're already set\n");
+ return E_FAIL;
+ }
+
+ trace("%08x: SetScriptSite(%08x) -----> Base thread is %08x\n", this, pass, tsrm_thread_id());
- trace("%08x: -----> Base thread is %08x\n", this, tsrm_thread_id());
+ if (pass) {
+ m_basethread = tsrm_thread_id();
+#if ACTIVEPHP_HAS_OWN_THREAD
+ HRESULT ret = GIT_put(pass, IID_IActiveScriptSite, &m_asscookie);
+#endif
+ }
if (m_pass) {
+#if ACTIVEPHP_HAS_OWN_THREAD
+ trace("killing off ass cookie\n");
+ GIT_revoke(m_asscookie, m_pass);
+#endif
m_pass->Release();
m_pass = NULL;
- SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, 0 TSRMLS_CC);
}
if (pass == NULL) {
trace("Closing down site; we should have no references to objects from the host\n"
- " m_pass=%08x\n m_pass_eng=%08x\n What about named items??\n",
- m_pass, m_pass_eng);
+ " m_pass=%08x\n What about named items??\n",
+ m_pass);
}
- m_pass = pass;
- if (m_pass) {
- m_pass->AddRef();
-
- DWORD cookie;
- if (SUCCEEDED(GIT_put(m_pass, IID_IActiveScriptSite, &cookie)))
- SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, cookie TSRMLS_CC);
+ if (pass) {
+ trace("taking a ref on pass\n");
+ ret = pass->QueryInterface(IID_IActiveScriptSite, (void**)&m_pass);
+ trace("----> %s", php_win_err(ret));
- if (m_scriptstate == SCRIPTSTATE_UNINITIALIZED)
- SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, SCRIPTSTATE_INITIALIZED TSRMLS_CC);
+ if (SUCCEEDED(ret)) {
+
+#if !ACTIVEPHP_HAS_OWN_THREAD
+ setup_engine_state();
+#endif
+ SetScriptState(SCRIPTSTATE_INITIALIZED);
+ }
}
- tsrm_mutex_unlock(m_mutex);
- return S_OK;
+ return ret;
}
STDMETHODIMP TPHPScriptingEngine::GetScriptSite(REFIID riid, void **ppvObject)
@@ -1512,87 +1254,148 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptSite(REFIID riid, void **ppvObject)
HRESULT ret = S_FALSE;
trace("%08x: GetScriptSite()\n", this);
- tsrm_mutex_lock(m_mutex);
+ if (m_pass) {
+ ASS_CALL(ret, QueryInterface, (riid, ppvObject))
+ }
- if (m_pass)
- ret = m_pass->QueryInterface(riid, ppvObject);
-
- tsrm_mutex_unlock(m_mutex);
return ret;
}
STDMETHODIMP TPHPScriptingEngine::SetScriptState(SCRIPTSTATE ss)
{
+ HRESULT dummy = E_UNEXPECTED;
+ int start_running = 0;
TSRMLS_FETCH();
- trace("%08x: SetScriptState(%s)\n", this, scriptstate_to_string(ss));
- return SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, ss TSRMLS_CC);
+
+ if (tsrm_thread_id() != m_enginethread)
+ return marshal_call(this, APHP_SetScriptState, 1, ss);
+
+ trace("%08x: SetScriptState(current=%s, new=%s)\n",
+ this,
+ scriptstate_to_string(m_scriptstate),
+ scriptstate_to_string(ss));
+
+ if (m_scriptstate == SCRIPTSTATE_INITIALIZED && (ss == SCRIPTSTATE_STARTED || ss == SCRIPTSTATE_CONNECTED))
+ start_running = 1;
+
+ m_scriptstate = ss;
+
+ if (start_running) {
+ /* run "main()", as described in the docs */
+ if (m_pass) {
+ ASS_CALL(dummy, OnEnterScript, ());
+ }
+ trace("%08x: apply execute main to m_frags\n", this);
+ m_in_main = 1;
+ m_stop_main = 0;
+ zend_hash_apply_with_argument(&m_frags, execute_main, this TSRMLS_CC);
+ m_in_main = 0;
+ trace("%08x: --- done execute main\n", this);
+
+ if (m_pass) {
+ ASS_CALL(dummy, OnLeaveScript, ());
+ }
+ }
+
+ /* inform host/site of the change */
+ if (m_pass) {
+#if 0
+ if (ss == SCRIPTSTATE_INITIALIZED) {
+ VARIANT varRes;
+ VariantInit(&varRes);
+
+// ASS_CALL(dummy, OnScriptTerminate, (&varRes, NULL));
+ }
+#endif
+
+ ASS_CALL(dummy, OnStateChange, (m_scriptstate));
+ }
+
+
+ return S_OK;
}
STDMETHODIMP TPHPScriptingEngine::GetScriptState(SCRIPTSTATE *pssState)
{
trace("%08x: GetScriptState(current=%s)\n", this, scriptstate_to_string(m_scriptstate));
- tsrm_mutex_lock(m_mutex);
*pssState = m_scriptstate;
- tsrm_mutex_unlock(m_mutex);
return S_OK;
}
STDMETHODIMP TPHPScriptingEngine::Close(void)
{
TSRMLS_FETCH();
+
+ trace("Close() refcount = %d\n", m_refcount);
- if (m_pass) {
+ if (m_scriptstate == SCRIPTSTATE_CLOSED)
+ return E_UNEXPECTED;
+
+ if (m_done_init) {
+
+ if (tsrm_thread_id() != m_enginethread)
+ return marshal_call(this, APHP_Close, 0);
+
+ m_done_init = 0;
+ m_scriptstate = SCRIPTSTATE_CLOSED;
+ zend_hash_destroy(&m_frags);
+ php_request_shutdown(NULL);
+ m_enginethread = 0;
+ }
+
+
+ if (m_pass && tsrm_thread_id() == m_basethread) {
+ m_pass->OnStateChange(m_scriptstate);
+ }
+
+ //GIT_revoke(m_asscookie, m_pass);
+ trace("%08x: release site \n", this);
+
+ if (m_pass && tsrm_thread_id() == m_basethread) {
m_pass->Release();
m_pass = NULL;
}
- SEND_THREAD_MESSAGE(this, PHPSE_CLOSE, 0, 0 TSRMLS_CC);
+
return S_OK;
}
-/* Add an item to global namespace.
- * This is called in the context of the base thread (or perhaps some other thread).
- * We want to be able to work with the object in the engine thread, so we marshal
- * it into a stream and let the engine thread deal with it.
- * This works quite nicely when PHP scripts call into the object; threading is
- * handled correctly. */
+/* The Host uses this to add objects to the global namespace. Some objects are
+ * intended to have their child properties globally visible, so we add those to
+ * the global namespace too. */
+
STDMETHODIMP TPHPScriptingEngine::AddNamedItem(LPCOLESTR pstrName, DWORD dwFlags)
{
- struct php_active_script_add_named_item_info info;
HRESULT res;
+ IUnknown *punk = NULL;
+ ITypeInfo *ti = NULL;
TSRMLS_FETCH();
- info.pstrName = pstrName;
- info.dwFlags = dwFlags;
-
- res = m_pass->GetItemInfo(pstrName, SCRIPTINFO_IUNKNOWN, &info.punk, NULL);
+ if (tsrm_thread_id() != m_enginethread)
+ return marshal_call(this, APHP_AddNamedItem, 2, pstrName, dwFlags);
+
+ TWideString name(pstrName);
+ trace("AddNamedItem: %s (%08x) m_pass=%08x\n", name.ansi_string(), dwFlags, m_pass);
+
+ res = m_pass->GetItemInfo(pstrName, SCRIPTINFO_IUNKNOWN, &punk, &ti);
if (SUCCEEDED(res)) {
-#if ACTIVEPHP_THREADING_MODE == COINIT_MULTITHREADED
- /* strangely, the GIT doesn't want to give the engine thread the interface
- * in this threading mode */
- SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
-#else
- IDispatch *disp;
- res = info.punk->QueryInterface(IID_IDispatch, (void**)&disp);
-
+ IDispatch *disp = NULL;
+
+ trace("ADD_NAMED_ITEM\n");
+
+ res = punk->QueryInterface(IID_IDispatch, (void**)&disp);
if (SUCCEEDED(res) && disp) {
- if (SUCCEEDED(GIT_put(disp, IID_IDispatch, &info.marshal))) {
- trace("put disp=%p into git with marshal ID of %x\n", disp, info.marshal);
- SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
- GIT_revoke(info.marshal, disp);
- }
+ add_to_global_namespace(disp, dwFlags, name.ansi_string() TSRMLS_CC);
disp->Release();
} else {
- trace("failed to get IDispatch from punk %s", php_win_err(res));
+ trace("Ouch: failed to get IDispatch for %s from GIT '%s'\n", name.ansi_string(), php_win_err(res));
}
- info.punk->Release();
-#endif
+
} else {
trace("failed to get named item, %s", php_win_err(res));
}
-
- return S_OK;
+ return res;
}
/* Bind to a type library */
@@ -1602,17 +1405,25 @@ STDMETHODIMP TPHPScriptingEngine::AddTypeLib(
/* [in] */ DWORD dwMinor,
/* [in] */ DWORD dwFlags)
{
- struct php_active_script_add_tlb_info info;
+ HRESULT ret;
+ ITypeLib *TypeLib;
TSRMLS_FETCH();
- info.rguidTypeLib = &rguidTypeLib;
- info.dwMajor = dwMajor;
- info.dwMinor = dwMinor;
- info.dwFlags = dwFlags;
+ if (tsrm_thread_id() != m_enginethread)
+ return marshal_call(this, APHP_AddTypeLib, 4, rguidTypeLib, dwMajor, dwMinor, dwFlags);
- SEND_THREAD_MESSAGE(this, PHPSE_ADD_TYPELIB, 0, (LPARAM)&info TSRMLS_CC);
+ ENGINE_THREAD_ONLY(IActiveScript, AddTypeLib);
+
+ trace("AddTypeLib\n");
+ ret = LoadRegTypeLib(rguidTypeLib, (USHORT)dwMajor, (USHORT)dwMinor, LANG_NEUTRAL, &TypeLib);
- return S_OK;
+ if (SUCCEEDED(ret)) {
+ php_com_import_typelib(TypeLib, CONST_CS, CP_ACP TSRMLS_CC);
+ TypeLib->Release();
+ }
+
+
+ return ret;
}
/* Returns an object representing the PHP Scripting Engine.
@@ -1624,34 +1435,40 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptDispatch(
/* [in] */ LPCOLESTR pstrItemName,
/* [out] */ IDispatch **ppdisp)
{
+ zend_function *func = NULL;
TSRMLS_FETCH();
- *ppdisp = NULL;
- struct php_active_script_get_dispatch_info info;
-
- info.pstrItemName = pstrItemName;
- info.dispatch = NULL;
-
- /* This hack is required because the host is likely to query us
- * for a dispatch if we use any of its objects from PHP script.
- * Since the engine thread will be waiting for the return from
- * a COM call, we need to deliberately poke a hole in thread
- * safety so that it is possible to read the symbol table from
- * outside the engine thread and give it a valid return value.
- * This is "safe" only in this instance, since we are not modifying
- * the engine state by looking up the dispatch (I hope).
- * The scripting engine rules pretty much guarantee that this
- * method is only called in the base thread.
- * This appears to only happen when we set the threading to
- * apartment. */
if (tsrm_thread_id() != m_enginethread) {
- tsrm_ls = m_tsrm_hack;
- trace("HEY: hacking thread safety!\n");
+ return marshal_call(this, APHP_GetScriptDispatch, 2, pstrItemName, ppdisp);
}
+
+ *ppdisp = NULL;
+
+ if (pstrItemName != NULL) {
+ zval **tmp;
+ TWideString itemname(pstrItemName);
+ trace("GetScriptDispatch %s\n", itemname.ansi_string());
+
+ /* Get that item from the global namespace.
+ * If it is an object, export it as a dispatchable object.
+ * */
+
+ if (zend_hash_find(&EG(symbol_table), itemname.ansi_string(),
+ itemname.ansi_len() + 1, (void**)&tmp) == SUCCESS) {
+ if (Z_TYPE_PP(tmp) == IS_OBJECT) {
+ *ppdisp = php_com_wrapper_export(*tmp TSRMLS_CC);
+ }
+ } else if (zend_hash_find(EG(function_table), itemname.ansi_string(),
+ itemname.ansi_len() + 1, (void**)&func) == SUCCESS) {
+ trace("The host wants a function, but we don't have one\n");
+ }
- if (S_OK == engine_thread_handler(PHPSE_GET_DISPATCH, 0, (LPARAM)&info, NULL TSRMLS_CC)) {
- GIT_get(info.dispatch, IID_IDispatch, (void**)ppdisp);
+ } else {
+ trace("GetScriptDispatch NULL\n");
+ /* This object represents PHP global namespace */
+ *ppdisp = (IDispatch*)this;
}
+
if (*ppdisp) {
return S_OK;
@@ -1662,10 +1479,8 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptDispatch(
STDMETHODIMP TPHPScriptingEngine::GetCurrentScriptThreadID(
/* [out] */ SCRIPTTHREADID *pstidThread)
{
-// tsrm_mutex_lock(m_mutex);
trace("%08x: GetCurrentScriptThreadID()\n", this);
- *pstidThread = m_enginethread;
-// tsrm_mutex_unlock(m_mutex);
+ *pstidThread = GetCurrentThreadId();//m_enginethread;
return S_OK;
}
@@ -1673,10 +1488,8 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptThreadID(
/* [in] */ DWORD dwWin32ThreadId,
/* [out] */ SCRIPTTHREADID *pstidThread)
{
-// tsrm_mutex_lock(m_mutex);
trace("%08x: GetScriptThreadID()\n", this);
*pstidThread = dwWin32ThreadId;
-// tsrm_mutex_unlock(m_mutex);
return S_OK;
}
@@ -1684,11 +1497,8 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptThreadState(
/* [in] */ SCRIPTTHREADID stidThread,
/* [out] */ SCRIPTTHREADSTATE *pstsState)
{
-// tsrm_mutex_lock(m_mutex);
-
trace("%08x: GetScriptThreadState()\n", this);
*pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
-
switch(stidThread) {
case SCRIPTTHREADID_BASE:
stidThread = m_basethread;
@@ -1702,7 +1512,6 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptThreadState(
} else if (stidThread == m_enginethread) {
*pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
}
-// tsrm_mutex_unlock(m_mutex);
return S_OK;
}
@@ -1724,31 +1533,34 @@ STDMETHODIMP TPHPScriptingEngine::InterruptScriptThread(
STDMETHODIMP TPHPScriptingEngine::Clone(
/* [out] */ IActiveScript **ppscript)
{
- TPHPScriptingEngine *cloned = new TPHPScriptingEngine;
- TSRMLS_FETCH();
+ HRESULT ret = E_FAIL;
+ IUnknown *punk;
- trace("%08x: Clone()\n", this);
-
- if (ppscript)
- *ppscript = NULL;
-
- if (cloned) {
- cloned->InitNew();
- SEND_THREAD_MESSAGE(cloned, PHPSE_CLONE, 0, (LPARAM)this TSRMLS_CC);
- trace("%08x: Cloned OK, returning cloned object ptr %08x\n", this, cloned);
- *ppscript = (IActiveScript*)cloned;
- return S_OK;
-
+ trace("************* Clone this[%08x]\n", this);
+
+ punk = create_scripting_engine(this);
+
+ if (punk) {
+ ret = punk->QueryInterface(IID_IActiveScript, (void**)ppscript);
+ punk->Release();
}
-
- return E_FAIL;
+
+ return ret;
}
STDMETHODIMP TPHPScriptingEngine::InitNew( void)
{
TSRMLS_FETCH();
- SEND_THREAD_MESSAGE(this, PHPSE_INIT_NEW, 0, 0 TSRMLS_CC);
+ trace("InitNew() this=%08x\n", this);
+ if (m_done_init) {
+ if (tsrm_thread_id() != m_enginethread)
+ return marshal_call(this, APHP_InitNew, 0);
+
+ zend_hash_destroy(&m_frags);
+ php_request_shutdown(NULL);
+ setup_engine_state();
+ }
return S_OK;
}
@@ -1765,22 +1577,102 @@ STDMETHODIMP TPHPScriptingEngine::AddScriptlet(
/* [out] */ BSTR *pbstrName,
/* [out] */ EXCEPINFO *pexcepinfo)
{
- struct php_active_script_add_scriptlet_info info;
+ HRESULT ret;
TSRMLS_FETCH();
- info.pstrDefaultName = pstrDefaultName;
- info.pstrCode = pstrCode;
- info.pstrItemName = pstrItemName;
- info.pstrSubItemName = pstrSubItemName;
- info.pstrEventName = pstrEventName;
- info.pstrDelimiter = pstrDelimiter;
- info.dwSourceContextCookie = dwSourceContextCookie;
- info.ulStartingLineNumber = ulStartingLineNumber;
- info.dwFlags = dwFlags;
- info.pbstrName = pbstrName;
- info.pexcepinfo = pexcepinfo;
-
- return SEND_THREAD_MESSAGE(this, PHPSE_ADD_SCRIPTLET, 0, (LPARAM)&info TSRMLS_CC);
+ if (tsrm_thread_id() != m_enginethread)
+ return marshal_call(this, APHP_AddScriptlet, 11, pstrDefaultName, pstrCode, pstrItemName, pstrSubItemName, pstrEventName, pstrDelimiter, dwSourceContextCookie, ulStartingLineNumber, dwFlags, pbstrName, pexcepinfo);
+
+ ENGINE_THREAD_ONLY(IActiveScriptParse, AddScriptlet);
+
+trace("AddScriptlet\n");
+
+ /* Parse/compile a chunk of script that will act as an event handler.
+ * If the host supports IActiveScriptParseProcedure, this code will
+ * not be called.
+ * The docs are (typically) vague: AFAICT, once the code has been
+ * compiled, we are supposed to arrange for an IConnectionPoint
+ * advisory connection to the item/subitem, once the script
+ * moves into SCRIPTSTATE_CONNECTED.
+ * That's a lot of work!
+ *
+ * FIXME: this is currently almost useless
+ * */
+
+ TWideString
+ default_name(pstrDefaultName),
+ code(pstrCode),
+ item_name(pstrItemName),
+ sub_item_name(pstrSubItemName),
+ event_name(pstrEventName),
+ delimiter(pstrDelimiter);
+
+ /* lets invent a function name for the scriptlet */
+ char sname[256];
+
+ /* should check if the name is already used! */
+ if (pstrDefaultName)
+ strcpy(sname, default_name.ansi_string());
+ else {
+ sname[0] = 0;
+ strcat(sname, "__");
+ if (pstrItemName) {
+ strcat(sname, item_name.ansi_string());
+ strcat(sname, "_");
+ }
+ if (pstrSubItemName) {
+ strcat(sname, sub_item_name.ansi_string());
+ strcat(sname, "_");
+ }
+ if (pstrEventName)
+ strcat(sname, event_name.ansi_string());
+ }
+
+
+ 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",
+ this, scriptstate_to_string(m_scriptstate),
+ default_name.safe_ansi_string(), code.safe_ansi_string(), item_name.safe_ansi_string(),
+ sub_item_name.safe_ansi_string(), event_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+ ulStartingLineNumber);
+
+
+ code_frag *frag = compile_code_fragment(
+ FRAG_SCRIPTLET,
+ sname,
+ pstrCode,
+ ulStartingLineNumber,
+ pexcepinfo,
+ this
+ TSRMLS_CC);
+
+ if (frag) {
+
+ frag->persistent = (dwFlags & SCRIPTTEXT_ISPERSISTENT);
+
+ zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+
+ /*
+ ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
+
+ disp->AddRef();
+ disp->m_frag = frag;
+ disp->m_procflags = info->dwFlags;
+ disp->m_engine = this;
+ frag->ptr = disp;
+
+ *ppdisp = disp;
+ */
+ ret = S_OK;
+ } else {
+ ret = DISP_E_EXCEPTION;
+ }
+
+ *pbstrName = TWideString::bstr_from_ansi(sname);
+
+ trace("%08x: done with scriptlet %s\n", this, sname);
+
+
+ return ret;
}
STDMETHODIMP TPHPScriptingEngine::ParseScriptText(
@@ -1794,20 +1686,74 @@ STDMETHODIMP TPHPScriptingEngine::ParseScriptText(
/* [out] */ VARIANT *pvarResult,
/* [out] */ EXCEPINFO *pexcepinfo)
{
- struct php_active_script_parse_info info;
+trace("ParseScriptText\n");
+ int doexec;
+ TWideString
+ code(pstrCode),
+ item_name(pstrItemName),
+ delimiter(pstrDelimiter);
+ HRESULT ret;
TSRMLS_FETCH();
- info.pstrCode = pstrCode;
- info.pstrItemName = pstrItemName;
- info.punkContext = punkContext;
- info.pstrDelimiter = pstrDelimiter;
- info.dwSourceContextCookie = dwSourceContextCookie;
- info.ulStartingLineNumber = ulStartingLineNumber;
- info.dwFlags = dwFlags;
- info.pvarResult = pvarResult;
- info.pexcepinfo = pexcepinfo;
-
- return SEND_THREAD_MESSAGE(this, PHPSE_PARSE_SCRIPT, 0, (LPARAM)&info TSRMLS_CC);
+ trace("pstrCode=%p pstrItemName=%p punkContext=%p pstrDelimiter=%p pvarResult=%p pexcepinfo=%p\n",
+ pstrCode, pstrItemName, punkContext, pstrDelimiter, pvarResult, pexcepinfo);
+
+ if (tsrm_thread_id() != m_enginethread) {
+ return marshal_call(this, APHP_ParseScriptText, 9,
+ pstrCode, pstrItemName, punkContext, pstrDelimiter,
+ dwSourceContextCookie, ulStartingLineNumber,
+ dwFlags, pvarResult, pexcepinfo);
+ }
+
+ trace("%08x: ParseScriptText:\n state=%s\ncode=%.*s\n item=%s\n delim=%s\n line=%d\n",
+ this, scriptstate_to_string(m_scriptstate),
+ code.m_ansi_strlen,
+ code.safe_ansi_string(), item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+ ulStartingLineNumber);
+
+ code_frag *frag = compile_code_fragment(
+ FRAG_MAIN,
+ dwFlags & SCRIPTTEXT_ISEXPRESSION ? FRAG_CREATE_FUNC : NULL,
+ pstrCode,
+ ulStartingLineNumber,
+ pexcepinfo,
+ this
+ TSRMLS_CC);
+
+ doexec = (dwFlags & SCRIPTTEXT_ISEXPRESSION) ||
+ m_scriptstate == SCRIPTSTATE_STARTED ||
+ m_scriptstate == SCRIPTSTATE_CONNECTED ||
+ m_scriptstate == SCRIPTSTATE_DISCONNECTED;
+
+ if (frag) {
+ frag->persistent = (dwFlags & SCRIPTTEXT_ISPERSISTENT);
+ ret = S_OK;
+
+ if (dwFlags & SCRIPTTEXT_ISEXPRESSION) {
+ if (m_scriptstate == SCRIPTSTATE_INITIALIZED) {
+ /* not allowed to execute code in this state */
+ ret = E_UNEXPECTED;
+ doexec = 0;
+ }
+ }
+
+ if (doexec) {
+ /* execute the code as an expression */
+ if (!execute_code_fragment(frag, pvarResult, pexcepinfo TSRMLS_CC))
+ ret = DISP_E_EXCEPTION;
+ }
+
+ zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+ } else {
+ ret = DISP_E_EXCEPTION;
+ }
+
+ if (pvarResult) {
+ VariantInit(pvarResult);
+ }
+
+
+ return ret;
}
STDMETHODIMP TPHPScriptingEngine::ParseProcedureText(
@@ -1822,37 +1768,69 @@ STDMETHODIMP TPHPScriptingEngine::ParseProcedureText(
/* [in] */ DWORD dwFlags,
/* [out] */ IDispatch **ppdisp)
{
- struct php_active_script_parse_proc_info info;
+trace("ParseProcedureText\n");
+ ENGINE_THREAD_ONLY(IActiveScriptParseProcedure, ParseProcedureText);
+ /* This is the IActiveScriptParseProcedure implementation.
+ * IE uses this to request for an IDispatch that it will invoke in
+ * response to some event, and tells us the code that it wants to
+ * run.
+ * We compile the code and pass it back a dispatch object.
+ * The object will then serialize access to the engine thread and
+ * execute the opcodes */
+ TWideString
+ formal_params(pstrFormalParams),
+ procedure_name(pstrProcedureName),
+ item_name(pstrItemName),
+ delimiter(pstrDelimiter);
HRESULT ret;
TSRMLS_FETCH();
- info.pstrCode = pstrCode;
- info.pstrFormalParams = pstrFormalParams;
- info.pstrProcedureName = pstrProcedureName;
- info.pstrItemName = pstrItemName;
- info.punkContext = punkContext;
- info.pstrDelimiter = pstrDelimiter;
- info.dwSourceContextCookie = dwSourceContextCookie;
- info.ulStartingLineNumber = ulStartingLineNumber;
- info.dwFlags = dwFlags;
+
+ trace("%08x: ParseProc:\n state=%s\nparams=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
+ this, scriptstate_to_string(m_scriptstate),
+ formal_params.ansi_string(), procedure_name.ansi_string(),
+ item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+ ulStartingLineNumber);
+
+ code_frag *frag = compile_code_fragment(
+ FRAG_PROCEDURE,
+ NULL,
+ pstrCode,
+ ulStartingLineNumber,
+ NULL,
+ this
+ TSRMLS_CC);
+
+ if (frag) {
+
+ frag->persistent = (dwFlags & SCRIPTTEXT_ISPERSISTENT);
+ zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+
+ ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
+
+ disp->m_frag = frag;
+ disp->m_procflags = dwFlags;
+ disp->m_engine = this;
+ frag->ptr = disp;
+
+ *ppdisp = (IDispatch*)disp;
+ } else {
+ ret = DISP_E_EXCEPTION;
+ }
- ret = SEND_THREAD_MESSAGE(this, PHPSE_PARSE_PROC, 0, (LPARAM)&info TSRMLS_CC);
- if (ret == S_OK)
- ret = GIT_get(info.dispcookie, IID_IDispatch, (void**)ppdisp);
-
trace("ParseProc: ret=%08x disp=%08x\n", ret, *ppdisp);
return ret;
}
-#if ACTIVEPHP_OBJECT_SAFETY
STDMETHODIMP TPHPScriptingEngine::GetInterfaceSafetyOptions(
/* [in] */ REFIID riid, // Interface that we want options for
/* [out] */ DWORD *pdwSupportedOptions, // Options meaningful on this interface
/* [out] */ DWORD *pdwEnabledOptions) // current option values on this interface
{
/* PHP isn't really safe for untrusted anything */
- *pdwSupportedOptions = 0;
+ trace("GetInterfaceSafetyOptions called\n");
+ *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACESAFE_FOR_UNTRUSTED_CALLER;
*pdwEnabledOptions = 0;
return S_OK;
}
@@ -1863,22 +1841,49 @@ STDMETHODIMP TPHPScriptingEngine::SetInterfaceSafetyOptions(
/* [in] */ DWORD dwEnabledOptions) // New option values
{
/* PHP isn't really safe for untrusted anything */
+ trace("SetInterfaceSafetyOptions mask=%08x enabled=%08x\n", dwOptionSetMask, dwEnabledOptions);
if (dwEnabledOptions == 0) {
return S_OK;
}
+ trace("can't set those options; we're not safe enough\n");
return E_FAIL;
}
-#endif
-
-extern "C"
-void activescript_error_func(int type, const char *error_msg, ...)
+
+#if 0
+STDMETHODIMP TPHPScriptingEngine::GetUnmarshalClass(
+ REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid)
{
- TSRMLS_FETCH();
+ return E_NOTIMPL;
+}
- TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
+STDMETHODIMP TPHPScriptingEngine::GetMarshalSizeMax(
+ REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, ULONG *pSize)
+{
+ return E_NOTIMPL;
+}
-
+STDMETHODIMP TPHPScriptingEngine::MarshalInterface(
+ IStream *pStm, REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshflags)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TPHPScriptingEngine::UnmarshalInterface(
+ IStream *pStm, REFIID riid, void **ppv)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TPHPScriptingEngine::ReleaseMarshalData(IStream *pStm)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TPHPScriptingEngine::DisconnectObject(DWORD dwReserved)
+{
+ return E_NOTIMPL;
}
+#endif
class TActiveScriptError:
public IActiveScriptError
@@ -1929,9 +1934,12 @@ public:
/* [out] */ ULONG *pulLineNumber,
/* [out] */ LONG *plCharacterPosition)
{
- *pdwSourceContext = 0;
- *pulLineNumber = m_lineno;
- *plCharacterPosition = 0;
+ if (pdwSourceContext)
+ *pdwSourceContext = 0;
+ if (pulLineNumber)
+ *pulLineNumber = m_lineno;
+ if (plCharacterPosition)
+ *plCharacterPosition = 0;
return S_OK;
}
@@ -1971,10 +1979,19 @@ void activescript_error_handler(int type, const char *error_filename,
int buflen;
TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
- buflen = vspprintf(&buf, PG(log_errors_max_len), format, args);
- trace("%08x: Error: %s\n", engine, buf);
-
- /* if it's a fatal error, report it using IActiveScriptError. */
+ /* ugly way to detect an exception for an eval'd fragment */
+ if (type == E_ERROR && EG(exception) && strstr("Exception thrown without a stack frame", format)) {
+ zend_exception_error(EG(exception) TSRMLS_CC);
+ /* NOTREACHED -- recursive E_ERROR */
+ } else {
+ buflen = vspprintf(&buf, PG(log_errors_max_len), format, args);
+ trace("%08x: PHP Error: %s\n", engine, buf);
+ }
+
+ TActiveScriptError *eobj = new TActiveScriptError(error_filename, error_lineno, buf);
+ trace("raising error object!\n");
+ if (engine->m_pass)
+ engine->m_pass->OnScriptError(eobj);
switch(type) {
case E_ERROR:
@@ -1982,16 +1999,8 @@ void activescript_error_handler(int type, const char *error_filename,
case E_COMPILE_ERROR:
case E_USER_ERROR:
case E_PARSE:
- /* trigger an error in the host */
- TActiveScriptError *eobj = new TActiveScriptError(error_filename, error_lineno, buf);
- trace("raising error object!\n");
- if (engine->m_pass_eng)
- engine->m_pass_eng->OnScriptError(eobj);
-
/* now throw the exception to abort execution */
- if (engine->m_err_trap)
- longjmp(*engine->m_err_trap, 1);
-
+ zend_bailout();
break;
}
efree(buf);