📄 scriptengine.cpp
字号:
/* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2006 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 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_01.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: scriptengine.cpp,v 1.4.16.1 2006/01/01 13:47:01 sniper Exp $ *//* Implementation Notes: * * PHP stores scripting engine state in thread-local storage. That means * that we need to create a dedicated thread per-engine so that a host can * use more than one engine object per thread. * * There are some interesting synchronization issues: Anything to do with * running script in the PHP/Zend engine must take place on the engine * thread. Likewise, calling back to the host must take place on the base * thread - the thread that set the script site. * */#define _WIN32_DCOM#include "php.h"extern "C" {#include "php_main.h"#include "SAPI.h"#include "zend.h"#include "zend_execute.h"#include "zend_compile.h"#include "php_globals.h"#include "php_variables.h"#include "php_ini.h"#include "php4activescript.h"#include "ext/com/com.h"#include "ext/com/php_COM.h"#include "ext/com/conversion.h"}#include "php_ticks.h"#include "php4as_scriptengine.h"#include "php4as_classfactory.h"#include <objbase.h>/* {{{ trace */static inline void trace(char *fmt, ...){ va_list ap; char buf[4096]; sprintf(buf, "T=%08x ", tsrm_thread_id()); OutputDebugString(buf); va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); OutputDebugString(buf); va_end(ap);}/* }}} *//* {{{ scriptstate_to_string */static const char *scriptstate_to_string(SCRIPTSTATE ss){ switch(ss) { case SCRIPTSTATE_UNINITIALIZED: return "SCRIPTSTATE_UNINITIALIZED"; case SCRIPTSTATE_INITIALIZED: return "SCRIPTSTATE_INITIALIZED"; case SCRIPTSTATE_STARTED: return "SCRIPTSTATE_STARTED"; case SCRIPTSTATE_CONNECTED: return "SCRIPTSTATE_CONNECTED"; case SCRIPTSTATE_DISCONNECTED: return "SCRIPTSTATE_DISCONNECTED"; case SCRIPTSTATE_CLOSED: return "SCRIPTSTATE_CLOSED"; default: return "unknown"; }}/* }}} *//* {{{ TWideString *//* This class helps manipulate strings from OLE. * It does not use emalloc, so it is better suited for passing pointers * between threads. */class TWideString { public: LPOLESTR m_ole; char *m_ansi; int m_ansi_strlen; TWideString(LPOLESTR olestr) { m_ole = olestr; m_ansi = NULL; } TWideString(LPCOLESTR olestr) { m_ole = (LPOLESTR)olestr; m_ansi = NULL; } ~TWideString() { if (m_ansi) { CoTaskMemFree(m_ansi); } m_ansi = NULL; } char *safe_ansi_string() { char *ret = ansi_string(); if (ret == NULL) return "<NULL>"; return ret; } int ansi_len(void) { /* force conversion if it has not already occurred */ if (m_ansi == NULL) ansi_string(); return m_ansi_strlen; } static BSTR bstr_from_ansi(char *ansi) { OLECHAR *ole = NULL; BSTR bstr = NULL; int req = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0); if (req) { ole = (OLECHAR*)CoTaskMemAlloc((req + 1) * sizeof(OLECHAR)); if (ole) { req = MultiByteToWideChar(CP_ACP, 0, ansi, -1, ole, req); req--; ole[req] = 0; bstr = SysAllocString(ole); CoTaskMemFree(ole); } } return bstr; } char *ansi_string(void) { if (m_ansi) return m_ansi; if (m_ole == NULL) return NULL; int bufrequired = WideCharToMultiByte(CP_ACP, 0, m_ole, -1, NULL, 0, NULL, NULL); if (bufrequired) { m_ansi = (char*)CoTaskMemAlloc(bufrequired + 1); if (m_ansi) { m_ansi_strlen = WideCharToMultiByte(CP_ACP, 0, m_ole, -1, m_ansi, bufrequired + 1, NULL, NULL); if (m_ansi_strlen) { m_ansi_strlen--; m_ansi[m_ansi_strlen] = 0; } else { trace("conversion failed with return code %08x\n", GetLastError()); } } } return m_ansi; }};/* }}} *//* {{{ code fragment structures */enum fragtype { FRAG_MAIN, FRAG_SCRIPTLET, FRAG_PROCEDURE};typedef struct { enum fragtype fragtype; zend_op_array *opcodes; char *code; int persistent; /* should be retained for Clone */ int executed; /* for "main" */ char *functionname; unsigned int codelen; unsigned int starting_line; TPHPScriptingEngine *engine; void *ptr;} code_frag;#define FRAG_CREATE_FUNC (char*)-1static code_frag *compile_code_fragment( enum fragtype fragtype, char *functionname, LPCOLESTR code, ULONG starting_line, EXCEPINFO *excepinfo, TPHPScriptingEngine *engine TSRMLS_DC);static int execute_code_fragment(code_frag *frag, VARIANT *varResult, EXCEPINFO *excepinfo TSRMLS_DC);static void free_code_fragment(code_frag *frag);static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC);/* }}} *//* 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. * Win95 without DCOM 1.1, and NT SP 2 or lower do not have * the GIT; so we emulate the GIT using other means. * If you trace problems back to this code, installing the relevant * SP should solve them. * */static inline HRESULT GIT_get(DWORD cookie, REFIID riid, void **obj){ IGlobalInterfaceTable *git; HRESULT ret; if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**)&git))) { ret = git->GetInterfaceFromGlobal(cookie, riid, obj); git->Release(); return ret; } return CoGetInterfaceAndReleaseStream((LPSTREAM)cookie, riid, obj);}static inline HRESULT GIT_put(IUnknown *unk, REFIID riid, DWORD *cookie){ IGlobalInterfaceTable *git; HRESULT ret; if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**)&git))) { ret = git->RegisterInterfaceInGlobal(unk, riid, cookie); git->Release(); return ret; } return CoMarshalInterThreadInterfaceInStream(riid, unk, (LPSTREAM*)cookie);}static inline HRESULT GIT_revoke(DWORD cookie, IUnknown *unk){ IGlobalInterfaceTable *git; HRESULT ret; if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**)&git))) { ret = git->RevokeInterfaceFromGlobal(cookie); git->Release(); } /* Kill remote clients */ return CoDisconnectObject(unk, 0);}/* {{{ A generic stupid IDispatch implementation */class IDispatchImpl: public IDispatch{protected: volatile LONG m_refcount;public: /* IUnknown */ STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject) { *ppvObject = NULL; if (IsEqualGUID(IID_IDispatch, iid)) { *ppvObject = (IDispatch*)this; } else if (IsEqualGUID(IID_IUnknown, iid)) { *ppvObject = this; } if (*ppvObject) { AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(DWORD) AddRef(void) { return InterlockedIncrement(const_cast<long*> (&m_refcount)); } STDMETHODIMP_(DWORD) Release(void) { DWORD ret = InterlockedDecrement(const_cast<long*> (&m_refcount)); trace("%08x: IDispatchImpl: release ref count is now %d\n", this, ret); if (ret == 0) delete this; return ret; } /* IDispatch */ STDMETHODIMP GetTypeInfoCount(unsigned int * pctinfo) { *pctinfo = 0; trace("%08x: IDispatchImpl: GetTypeInfoCount\n", this); return S_OK; } STDMETHODIMP GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo) { trace("%08x: IDispatchImpl: GetTypeInfo\n", this); return DISP_E_BADINDEX; } STDMETHODIMP GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID *rgDispId) { unsigned int i; trace("%08x: IDispatchImpl: GetIDsOfNames: \n", this); for (i = 0; i < cNames; i++) { TWideString name(rgszNames[i]); trace(" %s\n", name.ansi_string()); } trace("----\n"); return DISP_E_UNKNOWNNAME; } STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr) { trace("%08x: IDispatchImpl: Invoke dispid %08x\n", this, dispIdMember); return S_OK; } IDispatchImpl() { m_refcount = 1; } virtual ~IDispatchImpl() { }};/* }}} *//* {{{ 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{public: ScriptDispatch() { m_refcount = 1; }};/* }}} *//* {{{ This object is used in conjunction with IActiveScriptParseProcedure to * allow scriptlets to be bound to events. IE uses this for declaring * event handlers such as onclick="...". * The compiled code is stored in this object; IE will call * IDispatch::Invoke when the element is clicked. * */class ScriptProcedureDispatch: public IDispatchImpl{public: code_frag *m_frag; DWORD m_procflags; TPHPScriptingEngine *m_engine; DWORD m_gitcookie; STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr) { TSRMLS_FETCH(); 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); } return S_OK; } ScriptProcedureDispatch() { m_refcount = 1; GIT_put((IDispatch*)this, IID_IDispatch, &m_gitcookie); }};/* }}} *//* {{{ code fragment management */static code_frag *compile_code_fragment( enum fragtype fragtype, char *functionname, LPCOLESTR code, ULONG starting_line, EXCEPINFO *excepinfo, TPHPScriptingEngine *engine TSRMLS_DC){ zval pv; int code_offs = 0; char namebuf[256]; code_frag *frag = (code_frag*)CoTaskMemAlloc(sizeof(code_frag)); memset(frag, 0, sizeof(code_frag)); frag->engine = engine; /* handle the function name */ if (functionname) { int namelen; if (functionname == FRAG_CREATE_FUNC) { ULONG n = ++engine->m_lambda_count; sprintf(namebuf, "__frag_%08x_%u", engine, n); functionname = namebuf; } namelen = strlen(functionname); code_offs = namelen + sizeof("function (){"); frag->functionname = (char*)CoTaskMemAlloc((namelen + 1) * sizeof(char)); memcpy(frag->functionname, functionname, namelen+1); } frag->functionname = functionname; trace("%08x: COMPILED FRAG\n", frag); frag->codelen = WideCharToMultiByte(CP_ACP, 0, code, -1, NULL, 0, NULL, NULL); frag->code = (char*)CoTaskMemAlloc(sizeof(char) * (frag->codelen + code_offs + 1)); if (functionname) { sprintf(frag->code, "function %s(){ ", functionname); } frag->codelen = WideCharToMultiByte(CP_ACP, 0, code, -1, frag->code + code_offs, frag->codelen, NULL, NULL) - 1; if (functionname) { frag->codelen += code_offs + 1; frag->code[frag->codelen-1] = '}'; frag->code[frag->codelen] = 0; }trace("code to compile is:\ncode_offs=%d func=%s\n%s\n", code_offs, functionname, frag->code); frag->fragtype = fragtype; frag->starting_line = starting_line; pv.type = IS_STRING; pv.value.str.val = frag->code; pv.value.str.len = frag->codelen; frag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC); if (frag->opcodes == NULL) { free_code_fragment(frag);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -