Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "toonz/scriptengine.h"
Toshihiro Shimizu 890ddd
#include "toonz/scriptbinding.h"
Toshihiro Shimizu 890ddd
#include "toonz/scriptbinding_files.h"
Toshihiro Shimizu 890ddd
#include "trenderer.h"
Toshihiro Shimizu 890ddd
#include "toonz/toonzfolders.h"
Toshihiro Shimizu 890ddd
#include <qscriptengine></qscriptengine>
Toshihiro Shimizu 890ddd
#include <qscriptprogram></qscriptprogram>
Toshihiro Shimizu 890ddd
#include <qfile></qfile>
Toshihiro Shimizu 890ddd
#include <qtextstream></qtextstream>
Toshihiro Shimizu 890ddd
#include <qthread></qthread>
Toshihiro Shimizu 890ddd
#include <qmutex></qmutex>
Toshihiro Shimizu 890ddd
#include <qwaitcondition></qwaitcondition>
Toshihiro Shimizu 890ddd
#include <qtglobal></qtglobal>
Toshihiro Shimizu 890ddd
#include <qapplication></qapplication>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=========================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
namespace
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void sleep(unsigned long msec)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	QMutex mutex;
Toshihiro Shimizu 890ddd
	mutex.lock();
Toshihiro Shimizu 890ddd
	QWaitCondition waitCondition;
Toshihiro Shimizu 890ddd
	waitCondition.wait(&mutex, msec);
Toshihiro Shimizu 890ddd
	mutex.unlock();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
QString print(QScriptValue arg, bool addQuotes)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (arg.isArray()) {
Toshihiro Shimizu 890ddd
		QString s = "[";
Toshihiro Shimizu 890ddd
		quint32 len = arg.property(QLatin1String("length")).toUInt32();
Toshihiro Shimizu 890ddd
		for (quint32 i = 0; i < len; ++i) {
Toshihiro Shimizu 890ddd
			QScriptValue item = arg.property(i);
Toshihiro Shimizu 890ddd
			if (i > 0)
Toshihiro Shimizu 890ddd
				s += ",";
Toshihiro Shimizu 890ddd
			s += print(item, addQuotes);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
		s += "]";
Toshihiro Shimizu 890ddd
		return s;
Toshihiro Shimizu 890ddd
	} else if (arg.isBool()) {
Toshihiro Shimizu 890ddd
		return arg.toBool() ? "true" : "false";
Toshihiro Shimizu 890ddd
	} else if (arg.isString()) {
Toshihiro Shimizu 890ddd
		if (addQuotes)
Toshihiro Shimizu 890ddd
			return "\"" + arg.toString() + "\"";
Toshihiro Shimizu 890ddd
		else
Toshihiro Shimizu 890ddd
			return arg.toString();
Toshihiro Shimizu 890ddd
	} else
Toshihiro Shimizu 890ddd
		return arg.toString();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
QScriptValue printFunction(QScriptContext *context, QScriptEngine *engine)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	QString result;
Toshihiro Shimizu 890ddd
	for (int i = 0; i < context->argumentCount(); ++i) {
Toshihiro Shimizu 890ddd
		if (i > 0)
Toshihiro Shimizu 890ddd
			result.append(" ");
Toshihiro Shimizu 890ddd
		result.append(print(context->argument(i), false));
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
	QScriptValue calleeData = context->callee().data();
Toshihiro Shimizu 890ddd
	ScriptEngine *se = qobject_cast<scriptengine *="">(calleeData.toQObject());</scriptengine>
Toshihiro Shimizu 890ddd
	se->emitOutput(ScriptEngine::SimpleText, result);
Toshihiro Shimizu 890ddd
	sleep(50);
Toshihiro Shimizu 890ddd
	return se->voidValue();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
QScriptValue warningFunction(QScriptContext *context, QScriptEngine *engine)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	QString result;
Toshihiro Shimizu 890ddd
	for (int i = 0; i < context->argumentCount(); ++i) {
Toshihiro Shimizu 890ddd
		if (i > 0)
Toshihiro Shimizu 890ddd
			result.append(" ");
Toshihiro Shimizu 890ddd
		result.append(print(context->argument(i), false));
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
	QScriptValue calleeData = context->callee().data();
Toshihiro Shimizu 890ddd
	ScriptEngine *se = qobject_cast<scriptengine *="">(calleeData.toQObject());</scriptengine>
Toshihiro Shimizu 890ddd
	se->emitOutput(ScriptEngine::Warning, result);
Toshihiro Shimizu 890ddd
	sleep(50);
Toshihiro Shimizu 890ddd
	return se->voidValue();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
QScriptValue runFunction(QScriptContext *context, QScriptEngine *engine)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (context->argumentCount() != 1) {
Toshihiro Shimizu 890ddd
		return context->throwError("expected one parameter");
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
	TFilePath fp;
Toshihiro Shimizu 890ddd
	QScriptValue err = TScriptBinding::checkFilePath(context, context->argument(0), fp);
Toshihiro Shimizu 890ddd
	if (err.isError())
Toshihiro Shimizu 890ddd
		return err;
Toshihiro Shimizu 890ddd
	if (!fp.isAbsolute()) {
Toshihiro Shimizu 890ddd
		fp = ToonzFolder::getLibraryFolder() + "scripts" + fp;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QString fpStr = QString::fromStdWString(fp.getWideString());
Toshihiro Shimizu 890ddd
	QFile file(fpStr);
Toshihiro Shimizu 890ddd
	if (!file.open(QIODevice::ReadOnly)) {
Toshihiro Shimizu 890ddd
		return context->throwError("can't read file " + fpStr);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
	QTextStream in(&file);
Toshihiro Shimizu 890ddd
	QString content = in.readAll();
Toshihiro Shimizu 890ddd
	file.close();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QScriptProgram program(content, fpStr);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QScriptContext *parent = context->parentContext();
Toshihiro Shimizu 890ddd
	if (parent != 0) {
Toshihiro Shimizu 890ddd
		context->setActivationObject(context->parentContext()->activationObject());
Toshihiro Shimizu 890ddd
		context->setThisObject(context->parentContext()->thisObject());
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QScriptValue ret = engine->evaluate(program);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (engine->hasUncaughtException()) {
Toshihiro Shimizu 890ddd
		int line = engine->uncaughtExceptionLineNumber();
Toshihiro Shimizu 890ddd
		return context->throwError(QString("%1, at line %2 of %3").arg(ret.toString()).arg(line).arg(fpStr));
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	return ret;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
  QScriptValue glub(QScriptContext *context, QScriptEngine *engine)
Toshihiro Shimizu 890ddd
  {
Toshihiro Shimizu 890ddd
    QScriptValue global = engine->globalObject();
Toshihiro Shimizu 890ddd
    QScriptValue te = global.property("_engine");
Toshihiro Shimizu 890ddd
    ScriptEngine *se = qscriptvalue_cast<scriptengine*>(te);</scriptengine*>
Toshihiro Shimizu 890ddd
    se->postCommand(context->argument(0));
Toshihiro Shimizu 890ddd
    return 0;
Toshihiro Shimizu 890ddd
  }
Toshihiro Shimizu 890ddd
  */
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
} // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=========================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
class ScriptEngine::Executor : public QThread
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	ScriptEngine *m_engine;
Toshihiro Shimizu 890ddd
	QString m_cmd;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Toshihiro Shimizu 890ddd
	Executor(ScriptEngine *engine, const QString &cmd) : m_engine(engine), m_cmd(cmd) {}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void run()
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		m_engine->m_engine->collectGarbage();
Toshihiro Shimizu 890ddd
		QScriptValue result = m_engine->m_engine->evaluate(m_cmd);
Toshihiro Shimizu 890ddd
		if (result.isError()) {
Toshihiro Shimizu 890ddd
			m_engine->emitOutput(ScriptEngine::SyntaxError, result.toString());
Toshihiro Shimizu 890ddd
		} else if (result.isUndefined()) {
Toshihiro Shimizu 890ddd
			m_engine->emitOutput(ScriptEngine::UndefinedEvaluationResult, "undefined");
Toshihiro Shimizu 890ddd
		} else {
Toshihiro Shimizu 890ddd
			if (qscriptvalue_cast<tscriptbinding::void *="">(result)) {</tscriptbinding::void>
Toshihiro Shimizu 890ddd
			} else {
Toshihiro Shimizu 890ddd
				m_engine->emitOutput(ScriptEngine::EvaluationResult, print(result, true));
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
class ScriptEngine::MainThreadEvaluationData
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
public:
Toshihiro Shimizu 890ddd
	QMutex m_mutex;
Toshihiro Shimizu 890ddd
	QWaitCondition m_cond;
Toshihiro Shimizu 890ddd
	QScriptValue m_fun, m_args, m_result;
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=========================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
inline void defineFunction(ScriptEngine *se, const QString &name, QScriptEngine::FunctionSignature f)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	QScriptEngine *engine = se->getQScriptEngine();
Toshihiro Shimizu 890ddd
	QScriptValue fObj = engine->newFunction(f);
Toshihiro Shimizu 890ddd
	fObj.setData(engine->newQObject(se));
Toshihiro Shimizu 890ddd
	engine->globalObject().setProperty(name, fObj);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=========================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
ScriptEngine::ScriptEngine()
Toshihiro Shimizu 890ddd
	: m_executor(0), m_engine(new QScriptEngine())
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	// I must call TRenderer::initialize(), because a script could cause a rendering driven by a working thread
Toshihiro Shimizu 890ddd
	TRenderer::initialize();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_mainThreadEvaluationData = new MainThreadEvaluationData();
Toshihiro Shimizu 890ddd
	QScriptValue global = m_engine->globalObject();
Toshihiro Shimizu 890ddd
	QScriptValue ctor;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QScriptEngine &engine = *m_engine;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	defineFunction(this, "print", printFunction);
Toshihiro Shimizu 890ddd
	defineFunction(this, "warning", warningFunction);
Toshihiro Shimizu 890ddd
	defineFunction(this, "run", runFunction);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	/*
Toshihiro Shimizu 890ddd
  QScriptValue print = engine.newFunction(printFunction);
Toshihiro Shimizu 890ddd
  print.setData(engine.newQObject(this));
Toshihiro Shimizu 890ddd
  engine.globalObject().setProperty("print", print);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  QScriptValue print = engine.newFunction(printFunction);
Toshihiro Shimizu 890ddd
  print.setData(engine.newQObject(this));
Toshihiro Shimizu 890ddd
  engine.globalObject().setProperty("print", print);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  QScriptValue run = engine.newFunction(runFunction);
Toshihiro Shimizu 890ddd
  run.setData(engine.newQObject(this));
Toshihiro Shimizu 890ddd
  engine.globalObject().setProperty("run", run);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  */
Toshihiro Shimizu 890ddd
	//QScriptValue g = engine.newFunction(glub);
Toshihiro Shimizu 890ddd
	//g.setData(engine.newQObject(this));
Toshihiro Shimizu 890ddd
	//engine.globalObject().setProperty("glub", g);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// engine.globalObject().setProperty("_engine", engine.newQObject(this));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_voidValue = new QScriptValue();
Toshihiro Shimizu 890ddd
	*m_voidValue = engine.newQObject(new TScriptBinding::Void(), QScriptEngine::AutoOwnership);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	engine.globalObject().setProperty("void", *m_voidValue);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TScriptBinding::bindAll(engine);
Toshihiro Shimizu 890ddd
	bool ret = connect(this, SIGNAL(mainThreadEvaluationPosted()), this, SLOT(onMainThreadEvaluationPosted()));
Toshihiro Shimizu 890ddd
	assert(ret);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
ScriptEngine::~ScriptEngine()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	delete m_mainThreadEvaluationData;
Toshihiro Shimizu 890ddd
	delete m_voidValue;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
const QScriptValue &ScriptEngine::evaluateOnMainThread(const QScriptValue &fun, const QScriptValue &arguments)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	MainThreadEvaluationData *d = m_mainThreadEvaluationData;
Toshihiro Shimizu 890ddd
	QMutexLocker locker(&d->m_mutex);
Toshihiro Shimizu 890ddd
	d->m_fun = fun;
Toshihiro Shimizu 890ddd
	d->m_args = arguments;
Toshihiro Shimizu 890ddd
	d->m_result = QScriptValue();
Toshihiro Shimizu 890ddd
	emit mainThreadEvaluationPosted();
Toshihiro Shimizu 890ddd
	d->m_cond.wait(&d->m_mutex);
Toshihiro Shimizu 890ddd
	return d->m_result;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void ScriptEngine::onMainThreadEvaluationPosted()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	Q_ASSERT(qApp && qApp->thread() == QThread::currentThread());
Toshihiro Shimizu 890ddd
	MainThreadEvaluationData *d = m_mainThreadEvaluationData;
Toshihiro Shimizu 890ddd
	QMutexLocker locker(&d->m_mutex);
Toshihiro Shimizu 890ddd
	d->m_result = d->m_fun.call(d->m_fun, d->m_args);
Toshihiro Shimizu 890ddd
	d->m_cond.wakeOne();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void ScriptEngine::evaluate(const QString &cmd)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (m_executor)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
	m_executor = new Executor(this, cmd);
Toshihiro Shimizu 890ddd
	connect(m_executor, SIGNAL(finished()), this, SLOT(onTerminated()));
Toshihiro Shimizu 890ddd
	m_executor->start();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
bool ScriptEngine::isEvaluating() const
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	return m_engine->isEvaluating();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void ScriptEngine::interrupt()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_engine->abortEvaluation();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void ScriptEngine::onTerminated()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	emit evaluationDone();
Toshihiro Shimizu 890ddd
	delete m_executor;
Toshihiro Shimizu 890ddd
	m_executor = 0;
Toshihiro Shimizu 890ddd
}