summaryrefslogtreecommitdiff
path: root/sapi/activescript/scriptengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sapi/activescript/scriptengine.cpp')
-rw-r--r--sapi/activescript/scriptengine.cpp1735
1 files changed, 872 insertions, 863 deletions
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);