Commit fbb76e5a authored by mjs's avatar mjs
Browse files

Reviewed by Geoff.

	- fixed <rdar://problem/4214783> REGRESSION: kjs_fast_malloc crash due to lack of locking on multiple threads (seen selecting volumes in the installer)

	Make sure to lock using the InterpreterLock class in all places that need it
	(including anything that uses the collector, the parser, the protect count hash table,
	and anything that allocates via fast_malloc).

	Added assertions to ensure that main_thread_malloc and friends are
	only called on the main thread.

	Also changed main_thread_free to schedule a free on the main
	thread if called from a background thread. This contingency is
	rare, but unavoidable in the case that JavaScript runs on
	background threads, since then objects can be garbage collected on
	any thread.

        Test cases added: Node, this is impossible to reproduce in Safari without the use of
	PAC files.

        * Makefile.am:
        * khtml/dom/dom_misc.h:
        * khtml/ecma/kjs_events.cpp:
        (JSAbstractEventListener::handleEvent):
        (JSLazyEventListener::parseCode):
        (KJS::getDOMEvent):
        * khtml/ecma/kjs_events.h:
        * khtml/ecma/kjs_proxy.cpp:
        (KJSProxyImpl::~KJSProxyImpl):
        (KJSProxyImpl::evaluate):
        (KJSProxyImpl::clear):
        (KJSProxyImpl::createHTMLEventHandler):
        (KJSProxyImpl::initScript):
        * khtml/ecma/kjs_traversal.cpp:
        (JSNodeFilterCondition::acceptNode):
        * khtml/ecma/kjs_traversal.h:
        * khtml/ecma/kjs_window.cpp:
        (Window::clear):
        (ScheduledAction::execute):
        * khtml/ecma/kjs_window.h:
        * khtml/ecma/xmlhttprequest.cpp:
        (KJS::XMLHttpRequest::send):
        (KJS::XMLHttpRequest::abort):
        (KJS::XMLHttpRequest::slotFinished):
        * khtml/misc/main_thread_malloc.cpp:
        (khtml::main_thread_malloc):
        (khtml::main_thread_calloc):
        (khtml::main_thread_free):
        (khtml::main_thread_realloc):
        (khtml::initialize_scheduled_free_list):
        (khtml::drain_scheduled_free_list):
        (khtml::schedule_free_on_main_thread):
        (khtml::public_fREe):
        * kwq/KWQKHTMLPart.mm:
        (KWQKHTMLPart::bindingRootObject):
        (KWQKHTMLPart::windowScriptObject):
        (KWQKHTMLPart::saveLocationProperties):
        (KWQKHTMLPart::restoreLocationProperties):
        (KWQKHTMLPart::openURLFromPageCache):
        (KWQKHTMLPart::cleanupPluginRootObjects):
        * kwq/KWQPageState.mm:
        (-[KWQPageState clear]):
        * kwq/KWQTimer.h:
        * kwq/KWQTimer.mm:
        (-[KWQMainThreadPerformTarget initWithFunction:]):
        (-[KWQMainThreadPerformTarget callFunction:]):
        (QTimer::immediateSingleShotOnMainThread):
        * kwq/WebCoreBridge.mm:
        (-[WebCoreBridge saveDocumentToPageCache]):
        * kwq/WebCoreJavaScript.mm:
        (+[WebCoreJavaScript rootObjectClasses]):
        (+[WebCoreJavaScript garbageCollect]):
        * kwq/WebCoreScriptDebugger.mm:
        (-[WebCoreScriptCallFrame evaluateWebScript:]):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@10565 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 3bdd4952
2005-09-14 Maciej Stachowiak <mjs@apple.com>
Reviewed by Geoff.
- fixed <rdar://problem/4214783> REGRESSION: kjs_fast_malloc crash due to lack of locking on multiple threads (seen selecting volumes in the installer)
Make sure to lock using the InterpreterLock class in all places that need it
(including anything that uses the collector, the parser, the protect count hash table,
and anything that allocates via fast_malloc).
Added assertions to ensure that main_thread_malloc and friends are
only called on the main thread.
Also changed main_thread_free to schedule a free on the main
thread if called from a background thread. This contingency is
rare, but unavoidable in the case that JavaScript runs on
background threads, since then objects can be garbage collected on
any thread.
Test cases added: Node, this is impossible to reproduce in Safari without the use of
PAC files.
* Makefile.am:
* khtml/dom/dom_misc.h:
* khtml/ecma/kjs_events.cpp:
(JSAbstractEventListener::handleEvent):
(JSLazyEventListener::parseCode):
(KJS::getDOMEvent):
* khtml/ecma/kjs_events.h:
* khtml/ecma/kjs_proxy.cpp:
(KJSProxyImpl::~KJSProxyImpl):
(KJSProxyImpl::evaluate):
(KJSProxyImpl::clear):
(KJSProxyImpl::createHTMLEventHandler):
(KJSProxyImpl::initScript):
* khtml/ecma/kjs_traversal.cpp:
(JSNodeFilterCondition::acceptNode):
* khtml/ecma/kjs_traversal.h:
* khtml/ecma/kjs_window.cpp:
(Window::clear):
(ScheduledAction::execute):
* khtml/ecma/kjs_window.h:
* khtml/ecma/xmlhttprequest.cpp:
(KJS::XMLHttpRequest::send):
(KJS::XMLHttpRequest::abort):
(KJS::XMLHttpRequest::slotFinished):
* khtml/misc/main_thread_malloc.cpp:
(khtml::main_thread_malloc):
(khtml::main_thread_calloc):
(khtml::main_thread_free):
(khtml::main_thread_realloc):
(khtml::initialize_scheduled_free_list):
(khtml::drain_scheduled_free_list):
(khtml::schedule_free_on_main_thread):
(khtml::public_fREe):
* kwq/KWQKHTMLPart.mm:
(KWQKHTMLPart::bindingRootObject):
(KWQKHTMLPart::windowScriptObject):
(KWQKHTMLPart::saveLocationProperties):
(KWQKHTMLPart::restoreLocationProperties):
(KWQKHTMLPart::openURLFromPageCache):
(KWQKHTMLPart::cleanupPluginRootObjects):
* kwq/KWQPageState.mm:
(-[KWQPageState clear]):
* kwq/KWQTimer.h:
* kwq/KWQTimer.mm:
(-[KWQMainThreadPerformTarget initWithFunction:]):
(-[KWQMainThreadPerformTarget callFunction:]):
(QTimer::immediateSingleShotOnMainThread):
* kwq/WebCoreBridge.mm:
(-[WebCoreBridge saveDocumentToPageCache]):
* kwq/WebCoreJavaScript.mm:
(+[WebCoreJavaScript rootObjectClasses]):
(+[WebCoreJavaScript garbageCollect]):
* kwq/WebCoreScriptDebugger.mm:
(-[WebCoreScriptCallFrame evaluateWebScript:]):
2005-09-14 Maciej Stachowiak <mjs@apple.com>
=== WebCore-417 ===
2005-09-18 Eric Seidel <eseidel@apple.com>
No review needed, SVG build fix only.
......
......@@ -55,8 +55,12 @@ protected:
// An implementation object will delete itself, if it has
// no DOMObject referencing it, and deleteMe() returns true.
unsigned int _ref;
private:
DomShared(const DomShared &);
DomShared &operator=(const DomShared &);
};
}; // namespace
} // namespace
#endif
......@@ -18,8 +18,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <kjs/protected_object.h> // temporary, remove after landing interpreter lock patch
#include "kjs_events.h"
#include "kjs_events.lut.h"
......@@ -42,6 +40,7 @@ using namespace DOM::EventNames;
using DOM::AtomicString;
using DOM::ClipboardEventImpl;
using DOM::DocumentImpl;
using DOM::DOMString;
using DOM::EventImpl;
using DOM::EventListenerEvent;
using DOM::KeyboardEventImpl;
......@@ -87,7 +86,11 @@ void JSAbstractEventListener::handleEvent(EventListenerEvent ele, bool isWindowE
KJSProxy *proxy = 0;
if (part)
proxy = KJSProxy::proxy( part );
if (!proxy)
return;
InterpreterLock lock;
ScriptInterpreter *interpreter = static_cast<ScriptInterpreter *>(proxy->interpreter());
ExecState *exec = interpreter->globalExec();
......@@ -98,74 +101,65 @@ void JSAbstractEventListener::handleEvent(EventListenerEvent ele, bool isWindowE
handleEventFuncValue = listener->get(exec, "handleEvent");
if (handleEventFuncValue->isObject()) {
handleEventFunc = static_cast<ObjectImp *>(handleEventFuncValue);
if (handleEventFunc->implementsCall())
hasHandleEvent = true;
}
if (proxy && (listener->implementsCall() || hasHandleEvent)) {
ref();
List args;
args.append(getDOMEvent(exec,evt));
Window *window = static_cast<Window*>(win);
// Set the event we're handling in the Window object
window->setCurrentEvent(evt);
// ... and in the interpreter
interpreter->setCurrentEvent(evt);
ObjectImp *thisObj;
if (isWindowEvent) {
thisObj = win;
} else {
Interpreter::lock();
thisObj = static_cast<ObjectImp *>(getDOMNode(exec, evt->currentTarget()));
Interpreter::unlock();
}
Interpreter::lock();
ValueImp *retval;
if (hasHandleEvent)
retval = handleEventFunc->call(exec, listener, args);
else
retval = listener->call(exec, thisObj, args);
Interpreter::unlock();
window->setCurrentEvent( 0 );
interpreter->setCurrentEvent( 0 );
#if APPLE_CHANGES
if ( exec->hadException() ) {
Interpreter::lock();
char *message = exec->exception()->toObject(exec)->get(exec, messagePropertyName)->toString(exec).ascii();
int lineNumber = exec->exception()->toObject(exec)->get(exec, "line")->toInt32(exec);
QString sourceURL;
{
// put this in a block to make sure UString is deallocated inside the lock
UString uSourceURL = exec->exception()->toObject(exec)->get(exec, "sourceURL")->toString(exec);
sourceURL = uSourceURL.qstring();
}
Interpreter::unlock();
if (Interpreter::shouldPrintExceptions()) {
printf("(event handler):%s\n", message);
}
KWQ(part)->addMessageToConsole(message, lineNumber, sourceURL);
exec->clearException();
}
#else
if ( exec->hadException() )
exec->clearException();
#endif
else if (html)
{
QVariant ret = ValueToVariant(exec, retval);
if (listener->implementsCall() || hasHandleEvent) {
ref();
List args;
args.append(getDOMEvent(exec,evt));
Window *window = static_cast<Window*>(win);
// Set the event we're handling in the Window object
window->setCurrentEvent(evt);
// ... and in the interpreter
interpreter->setCurrentEvent(evt);
ObjectImp *thisObj;
if (isWindowEvent) {
thisObj = win;
} else {
thisObj = static_cast<ObjectImp *>(getDOMNode(exec, evt->currentTarget()));
}
ValueImp *retval;
if (hasHandleEvent)
retval = handleEventFunc->call(exec, listener, args);
else
retval = listener->call(exec, thisObj, args);
window->setCurrentEvent( 0 );
interpreter->setCurrentEvent( 0 );
if ( exec->hadException() ) {
char *message = exec->exception()->toObject(exec)->get(exec, messagePropertyName)->toString(exec).ascii();
int lineNumber = exec->exception()->toObject(exec)->get(exec, "line")->toInt32(exec);
QString sourceURL;
{
// put this in a block to make sure UString is deallocated inside the lock
UString uSourceURL = exec->exception()->toObject(exec)->get(exec, "sourceURL")->toString(exec);
sourceURL = uSourceURL.qstring();
}
if (Interpreter::shouldPrintExceptions()) {
printf("(event handler):%s\n", message);
}
KWQ(part)->addMessageToConsole(message, lineNumber, sourceURL);
if (Interpreter::shouldPrintExceptions())
printf("(event handler):%s\n", message);
exec->clearException();
} else if (html) {
QVariant ret = ValueToVariant(exec, retval);
if (ret.type() == QVariant::Bool && ret.toBool() == false)
evt->preventDefault();
}
DOM::DocumentImpl::updateDocumentsRendering();
deref();
}
}
DOM::DocumentImpl::updateDocumentsRendering();
deref();
}
DOM::DOMString JSAbstractEventListener::eventListenerType()
......@@ -302,19 +296,16 @@ void JSLazyEventListener::parseCode() const
ScriptInterpreter *interpreter = static_cast<ScriptInterpreter *>(proxy->interpreter());
ExecState *exec = interpreter->globalExec();
Interpreter::lock();
//Constructor constr(Global::current().get("Function"));
InterpreterLock lock;
ObjectImp *constr = interpreter->builtinFunction();
List args;
static ProtectedValue eventString = String("event");
static ProtectedPtr<ValueImp> eventString = String("event");
UString sourceURL(part->m_url.url());
args.append(eventString);
args.append(String(code));
listener = constr->construct(exec, args, sourceURL, lineNumber); // ### is globalExec ok ?
Interpreter::unlock();
if (exec->hadException()) {
exec->clearException();
......@@ -330,10 +321,7 @@ void JSLazyEventListener::parseCode() const
Interpreter::unlock();
if (thisObj) {
Interpreter::lock();
static_cast<DOMNode*>(thisObj)->pushEventHandlerScope(exec, scope);
Interpreter::unlock();
listener->setScope(scope);
}
}
......@@ -555,7 +543,7 @@ ValueImp *getDOMEvent(ExecState *exec, EventImpl *e)
return Null();
ScriptInterpreter* interp = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
Interpreter::lock();
InterpreterLock lock;
DOMObject *ret = interp->getDOMObject(e);
if (!ret) {
......@@ -575,8 +563,6 @@ ValueImp *getDOMEvent(ExecState *exec, EventImpl *e)
interp->putDOMObject(e, ret);
}
Interpreter::unlock();
return ret;
}
......
// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
......@@ -19,13 +18,14 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _KJS_EVENTS_H_
#define _KJS_EVENTS_H_
#ifndef KJS_EVENTS_H
#define KJS_EVENTS_H
#include "kjs_dom.h"
#include "kjs_html.h"
#include "dom/dom2_events.h"
#include <kjs/protect.h>
namespace DOM {
class ClipboardImpl;
......@@ -76,8 +76,8 @@ namespace KJS {
virtual ObjectImp *windowObj() const;
void clearWindowObj();
protected:
mutable ProtectedObject listener;
ProtectedObject win;
mutable ProtectedPtr<ObjectImp> listener;
ProtectedPtr<ObjectImp> win;
};
class JSLazyEventListener : public JSEventListener {
......
// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
......@@ -30,6 +29,8 @@
using namespace KJS;
using DOM::EventListener;
extern "C" {
KJSProxy *kjs_html_init(KHTMLPart *khtmlpart);
}
......@@ -75,20 +76,22 @@ KJSProxyImpl::KJSProxyImpl(KHTMLPart *part)
KJSProxyImpl::~KJSProxyImpl()
{
//kdDebug() << "KJSProxyImpl::~KJSProxyImpl deleting interpreter " << m_script << endl;
InterpreterLock lock;
delete m_script;
#ifndef NDEBUG
s_count--;
// If it was the last interpreter, we should have nothing left
#ifdef KJS_DEBUG_MEM
if ( s_count == 0 )
if (s_count == 0)
Interpreter::finalCheck();
#endif
#endif
}
QVariant KJSProxyImpl::evaluate(QString filename, int baseLine,
const QString&str, DOM::NodeImpl *n) {
const QString&str, DOM::NodeImpl *n)
{
// evaluate code. Returns the JS return value or an invalid QVariant
// if there was none, an error occured or the type couldn't be converted.
......@@ -98,42 +101,32 @@ QVariant KJSProxyImpl::evaluate(QString filename, int baseLine,
// expected value in all cases.
// See smart window.open policy for where this is used.
bool inlineCode = filename.isNull();
//kdDebug(6070) << "KJSProxyImpl::evaluate inlineCode=" << inlineCode << endl;
#ifdef KJS_DEBUGGER
// ### KJSDebugWin::instance()->attach(m_script);
if (inlineCode)
filename = "(unknown file)";
if (KJSDebugWin::instance())
KJSDebugWin::instance()->setNextSourceInfo(filename,baseLine);
// KJSDebugWin::instance()->setMode(KJS::Debugger::Step);
#else
Q_UNUSED(baseLine);
#endif
m_script->setInlineCode(inlineCode);
KJS::ValueImp *thisNode = n ? Window::retrieve(m_part) : getDOMNode(m_script->globalExec(), n);
KJS::Interpreter::lock();
UString code( str );
KJS::Interpreter::unlock();
InterpreterLock lock;
KJS::ValueImp *thisNode = n ? Window::retrieve(m_part) : getDOMNode(m_script->globalExec(), n);
UString code(str);
Completion comp = m_script->evaluate(filename, baseLine, code, thisNode);
bool success = ( comp.complType() == Normal ) || ( comp.complType() == ReturnValue );
#ifdef KJS_DEBUGGER
// KJSDebugWin::instance()->setCode(QString::null);
#endif
// let's try to convert the return value
if (success && comp.value())
return ValueToVariant(m_script->globalExec(), comp.value());
if ( comp.complType() == Throw ) {
KJS::Interpreter::lock();
UString errorMessage = comp.value()->toString(m_script->globalExec());
int lineNumber = comp.value()->toObject(m_script->globalExec())->get(m_script->globalExec(), "line")->toInt32(m_script->globalExec());
UString sourceURL = comp.value()->toObject(m_script->globalExec())->get(m_script->globalExec(), "sourceURL")->toString(m_script->globalExec());
KJS::Interpreter::unlock();
#if APPLE_CHANGES
KWQ(m_part)->addMessageToConsole(errorMessage.qstring(), lineNumber, sourceURL.qstring());
......@@ -153,7 +146,6 @@ void KJSProxyImpl::clear() {
KJSDebugWin *debugWin = KJSDebugWin::instance();
if (debugWin && debugWin->currentScript() == m_script) {
debugWin->setMode(KJSDebugWin::Stop);
// debugWin->leaveSession();
}
#endif
Window *win = Window::retrieveWindow(m_part);
......@@ -172,6 +164,7 @@ DOM::EventListener *KJSProxyImpl::createHTMLEventHandler(QString sourceUrl, QStr
#endif
initScript();
InterpreterLock lock;
return KJS::Window::retrieveWindow(m_part)->getJSLazyEventListener(code,node,m_handlerLineno);
}
......@@ -265,9 +258,8 @@ void KJSProxyImpl::initScript()
return;
// Build the global object - which is a Window instance
KJS::Interpreter::lock();
KJS::InterpreterLock lock;
ObjectImp *globalObject( new Window(m_part) );
KJS::Interpreter::unlock();
// Create a KJS interpreter for this part
m_script = new KJS::ScriptInterpreter(globalObject, m_part);
......@@ -276,9 +268,7 @@ void KJSProxyImpl::initScript()
m_script->setDebuggingEnabled(m_debugEnabled);
#endif
//m_script->enableDebug();
KJS::Interpreter::lock();
globalObject->put(m_script->globalExec(), "debug", new TestFunctionImp(), Internal);
KJS::Interpreter::unlock();
#if APPLE_CHANGES
QString userAgent = KWQ(m_part)->userAgent();
......
// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
......@@ -20,12 +19,14 @@
#include "kjs_traversal.h"
#include "kjs_traversal.lut.h"
#include "kjs_proxy.h"
#include <dom/dom_node.h>
#include <xml/dom_nodeimpl.h>
#include <xml/dom_docimpl.h>
#include <khtmlview.h>
#include <kdebug.h>
#include <kjs/protect.h>
using DOM::FilterNode;
using DOM::NodeFilterImpl;
......@@ -322,6 +323,7 @@ short JSNodeFilterCondition::acceptNode(FilterNode filterNode) const
KHTMLPart *part = node->getDocument()->part();
KJSProxy *proxy = KJSProxy::proxy(part);
if (proxy && filter->implementsCall()) {
InterpreterLock lock;
ExecState *exec = proxy->interpreter()->globalExec();
List args;
args.append(getDOMNode(exec, node));
......
......@@ -23,7 +23,7 @@
#include "kjs_dom.h"
#include "dom/dom2_traversal.h"
#include <kjs/protected_object.h>
#include "kjs/protect.h"
namespace DOM {
class NodeFilterImpl;
......@@ -103,9 +103,9 @@ namespace KJS {
virtual ~JSNodeFilterCondition() {}
virtual short acceptNode(DOM::FilterNode) const;
protected:
ProtectedObject filter;
ProtectedPtr<ObjectImp> filter;
};
}; // namespace
} // namespace
#endif
......@@ -1488,21 +1488,21 @@ JSLazyEventListener *Window::getJSLazyEventListener(const QString& code, DOM::No
void Window::clear( ExecState *exec )
{
Interpreter::lock();
InterpreterLock lock;
if (m_returnValueSlot)
if (ValueImp *returnValue = getDirect("returnValue"))
*m_returnValueSlot = returnValue;
kdDebug(6070) << "Window::clear " << this << endl;
delete winq;
winq = new WindowQObject(this);
// Get rid of everything, those user vars could hold references to DOM nodes
clearProperties();
// Really delete those properties, so that the DOM nodes get deref'ed
// there's likely to be lots of garbage now
Collector::collect();
// Now recreate a working global object for the next URL that will use us
Interpreter *interpreter = KJSProxy::proxy( m_part )->interpreter();
interpreter->initGlobalObject();
Interpreter::unlock();
}
void Window::setCurrentEvent(EventImpl *evt)
......@@ -2044,43 +2044,36 @@ ScheduledAction::ScheduledAction(const QString &_code, bool _singleShot)
void ScheduledAction::execute(Window *window)
{
ScriptInterpreter *interpreter = static_cast<ScriptInterpreter *>(KJSProxy::proxy(window->m_part)->interpreter());
interpreter->setProcessingTimerCallback(true);
//kdDebug(6070) << "ScheduledAction::execute " << this << endl;
if (isFunction) {
if (func->implementsCall()) {
// #### check this
Q_ASSERT( window->m_part );
if ( window->m_part )
{
Interpreter *interpreter = KJSProxy::proxy( window->m_part )->interpreter();
Q_ASSERT(window->m_part);
if (window->m_part) {
Interpreter *interpreter = KJSProxy::proxy(window->m_part)->interpreter();
ExecState *exec = interpreter->globalExec();
Q_ASSERT( window == interpreter->globalObject() );
ObjectImp *obj( window );
Interpreter::lock();
func->call(exec,obj,args); // note that call() creates its own execution state for the func call
Interpreter::unlock();
if ( exec->hadException() ) {
#if APPLE_CHANGES
Interpreter::lock();
Q_ASSERT(window == interpreter->globalObject());
ObjectImp *obj(window);
InterpreterLock lock;
func->call(exec, obj, args); // note that call() creates its own execution state for the func call
if (exec->hadException()) {
char *message = exec->exception()->toObject(exec)->get(exec, messagePropertyName)->toString(exec).ascii();
int lineNumber = exec->exception()->toObject(exec)->get(exec, "line")->toInt32(exec);
Interpreter::unlock();
int lineNumber = exec->exception()->toObject(exec)->get(exec, "line")->toInt32(exec);
if (Interpreter::shouldPrintExceptions()) {
printf("(timer):%s\n", message);
}
KWQ(window->m_part)->addMessageToConsole(message, lineNumber, QString());
#endif
KWQ(window->m_part)->addMessageToConsole(message, lineNumber, QString());
}
exec->clearException();
}
}
}
}
}
else {
} else
window->m_part->executeScript(code);
}
// Update our document's rendering following the execution of the timeout callback.
if (DocumentImpl *doc = window->m_part->xmlDocImpl())
doc->updateRendering();
......
......@@ -27,7 +27,7 @@
#include <qptrdict.h>
#include "kjs_binding.h"
#include <kjs/protected_object.h>
#include <kjs/protect.h>
class QTimer;
class KHTMLView;
......@@ -191,7 +191,7 @@ namespace KJS {
ScheduledAction(const QString &_code, bool _singleShot);
void execute(Window *window);
ProtectedObject func;
ProtectedPtr<ObjectImp> func;
List args;
QString code;
bool isFunction;
......