History

rework ScriptValue to act like a stack variable rather than a pointer

Heather Anderson committed 5 years ago

Commit 85810221 parent f209c51170 files · +2837 −2594Browse files

Showing the changes to libraries/script-engine/src/ScriptManager.cpp (1 of 170 files). Show all

Unified · Split

libraries/script-engine/src/ScriptManager.cpp+168−168
@@ -87,7 +87,7 @@ int scriptManagerPointerMetaID = qRegisterMetaType<ScriptManagerPointer>(); · 86 unmodified lines
87 87
88Q_DECLARE_METATYPE(ExternalResource::Bucket);88Q_DECLARE_METATYPE(ExternalResource::Bucket);
89 89
90Q_DECLARE_METATYPE(ScriptValuePointer);90Q_DECLARE_METATYPE(ScriptValue);
91 91
92// --- Static script initialization registry92// --- Static script initialization registry
93 93
@@ -109,21 +109,21 @@ static void runStaticInitializers(ScriptManager* manager) { · 15 unmodified lines
109 109
110// ---110// ---
111 111
112static ScriptValuePointer debugPrint(ScriptContext* context, ScriptEngine* engine) {112static ScriptValue debugPrint(ScriptContext* context, ScriptEngine* engine) {
113 // assemble the message by concatenating our arguments113 // assemble the message by concatenating our arguments
114 QString message = "";114 QString message = "";
115 for (int i = 0; i < context->argumentCount(); i++) {115 for (int i = 0; i < context->argumentCount(); i++) {
116 if (i > 0) {116 if (i > 0) {
117 message += " ";117 message += " ";
118 }118 }
119 message += context->argument(i)->toString();119 message += context->argument(i).toString();
120 }120 }
121 121
122 // was this generated by a script engine? If we don't recognize it then send the message and exit122 // was this generated by a script engine? If we don't recognize it then send the message and exit
123 ScriptManager* scriptManager = engine->manager();123 ScriptManager* scriptManager = engine->manager();
124 if (!scriptManager) {124 if (!scriptManager) {
125 qCDebug(scriptengine_script, "%s", qUtf8Printable(message));125 qCDebug(scriptengine_script, "%s", qUtf8Printable(message));
126 return ScriptValuePointer();126 return ScriptValue();
127 }127 }
128 128
129 // This message was sent by one of our script engines, let's try to see if we can find the source.129 // This message was sent by one of our script engines, let's try to see if we can find the source.
@@ -170,7 +170,7 @@ static ScriptValuePointer debugPrint(ScriptContext* context, ScriptEngine* engin · 40 unmodified lines
170 qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(scriptManager->getFilename()), qUtf8Printable(message));170 qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(scriptManager->getFilename()), qUtf8Printable(message));
171 }171 }
172 172
173 return ScriptValuePointer();173 return ScriptValue();
174}174}
175 175
176// FIXME Come up with a way to properly encode entity IDs in filename176// FIXME Come up with a way to properly encode entity IDs in filename
@@ -193,7 +193,7 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) · 16 unmodified lines
193 return url + " [EntityID:" + entityID + "]";193 return url + " [EntityID:" + entityID + "]";
194}194}
195 195
196QString ScriptManager::logException(const ScriptValuePointer& exception) {196QString ScriptManager::logException(const ScriptValue& exception) {
197 auto message = formatException(exception, _enableExtendedJSExceptions.get());197 auto message = formatException(exception, _enableExtendedJSExceptions.get());
198 scriptErrorMessage(message);198 scriptErrorMessage(message);
199 return message;199 return message;
@@ -249,10 +249,10 @@ ScriptManager::ScriptManager(Context context, const QString& scriptContents, con · 49 unmodified lines
249 }249 }
250 250
251 // this is where all unhandled exceptions end up getting logged251 // this is where all unhandled exceptions end up getting logged
252 connect(this, &ScriptManager::unhandledException, this, [this](const ScriptValuePointer& err) {252 connect(this, &ScriptManager::unhandledException, this, [this](const ScriptValue& err) {
253 auto output = err->engine() == _engine ? err : _engine->makeError(err);253 auto output = err.engine() == _engine ? err : _engine->makeError(err);
254 if (!output->property("detail")->isValid()) {254 if (!output.property("detail").isValid()) {
255 output->setProperty("detail", "UnhandledException");255 output.setProperty("detail", "UnhandledException");
256 }256 }
257 logException(output);257 logException(output);
258 });258 });
@@ -516,10 +516,10 @@ void ScriptManager::clearDebugLogWindow() { · 257 unmodified lines
516// Templated qScriptRegisterMetaType fails to compile with raw pointers516// Templated qScriptRegisterMetaType fails to compile with raw pointers
517using ScriptableResourceRawPtr = ScriptableResource*;517using ScriptableResourceRawPtr = ScriptableResource*;
518 518
519static ScriptValuePointer scriptableResourceToScriptValue(ScriptEngine* engine,519static ScriptValue scriptableResourceToScriptValue(ScriptEngine* engine,
520 const ScriptableResourceRawPtr& resource) {520 const ScriptableResourceRawPtr& resource) {
521 if (!resource) {521 if (!resource) {
522 return ScriptValuePointer(); // probably shutting down522 return ScriptValue(); // probably shutting down
523 }523 }
524 524
525 // The first script to encounter this resource will track its memory.525 // The first script to encounter this resource will track its memory.
@@ -538,8 +538,8 @@ static ScriptValuePointer scriptableResourceToScriptValue(ScriptEngine* engine, · 12 unmodified lines
538 return object;538 return object;
539}539}
540 540
541static void scriptableResourceFromScriptValue(const ScriptValuePointer& value, ScriptableResourceRawPtr& resource) {541static void scriptableResourceFromScriptValue(const ScriptValue& value, ScriptableResourceRawPtr& resource) {
542 resource = static_cast<ScriptableResourceRawPtr>(value->toQObject());542 resource = static_cast<ScriptableResourceRawPtr>(value.toQObject());
543}543}
544 544
545/*@jsdoc545/*@jsdoc
@@ -555,7 +555,7 @@ static void scriptableResourceFromScriptValue(const ScriptValuePointer& value, S · 9 unmodified lines
555 *555 *
556 * @property {Resource.State} State - The possible loading states of a resource. <em>Read-only.</em>556 * @property {Resource.State} State - The possible loading states of a resource. <em>Read-only.</em>
557 */557 */
558static ScriptValuePointer createScriptableResourcePrototype(ScriptManagerPointer manager) {558static ScriptValue createScriptableResourcePrototype(ScriptManagerPointer manager) {
559 auto engine = manager->engine();559 auto engine = manager->engine();
560 auto prototype = engine->newObject();560 auto prototype = engine->newObject();
561 561
@@ -569,17 +569,17 @@ static ScriptValuePointer createScriptableResourcePrototype(ScriptManagerPointer · 7 unmodified lines
569 569
570 auto prototypeState = engine->newQObject(state, ScriptEngine::QtOwnership,570 auto prototypeState = engine->newQObject(state, ScriptEngine::QtOwnership,
571 ScriptEngine::ExcludeSlots | ScriptEngine::ExcludeSuperClassMethods);571 ScriptEngine::ExcludeSlots | ScriptEngine::ExcludeSuperClassMethods);
572 prototype->setProperty("State", prototypeState);572 prototype.setProperty("State", prototypeState);
573 573
574 return prototype;574 return prototype;
575}575}
576 576
577ScriptValuePointer externalResourceBucketToScriptValue(ScriptEngine* engine, ExternalResource::Bucket const& in) {577ScriptValue externalResourceBucketToScriptValue(ScriptEngine* engine, ExternalResource::Bucket const& in) {
578 return engine->newValue((int)in);578 return engine->newValue((int)in);
579}579}
580 580
581void externalResourceBucketFromScriptValue(const ScriptValuePointer& object, ExternalResource::Bucket& out) {581void externalResourceBucketFromScriptValue(const ScriptValue& object, ExternalResource::Bucket& out) {
582 out = static_cast<ExternalResource::Bucket>(object->toInt32());582 out = static_cast<ExternalResource::Bucket>(object.toInt32());
583}583}
584 584
585void ScriptManager::resetModuleCache(bool deleteScriptCache) {585void ScriptManager::resetModuleCache(bool deleteScriptCache) {
@@ -587,33 +587,33 @@ void ScriptManager::resetModuleCache(bool deleteScriptCache) { · 1 unmodified line
587 executeOnScriptThread([=]() { resetModuleCache(deleteScriptCache); });587 executeOnScriptThread([=]() { resetModuleCache(deleteScriptCache); });
588 return;588 return;
589 }589 }
590 auto jsRequire = _engine->globalObject()->property("Script")->property("require");590 auto jsRequire = _engine->globalObject().property("Script").property("require");
591 auto cache = jsRequire->property("cache");591 auto cache = jsRequire.property("cache");
592 auto cacheMeta = jsRequire->data();592 auto cacheMeta = jsRequire.data();
593 593
594 if (deleteScriptCache) {594 if (deleteScriptCache) {
595 auto it = cache->newIterator();595 auto it = cache.newIterator();
596 while (it->hasNext()) {596 while (it->hasNext()) {
597 it->next();597 it->next();
598 if (it->flags() & ScriptValue::SkipInEnumeration) {598 if (it->flags() & ScriptValue::SkipInEnumeration) {
599 continue;599 continue;
600 }600 }
601 qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it->name() << " for cache reset at next require";601 qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it->name() << " for cache reset at next require";
602 cacheMeta->setProperty(it->name(), true);602 cacheMeta.setProperty(it->name(), true);
603 }603 }
604 }604 }
605 cache = _engine->newObject();605 cache = _engine->newObject();
606 if (!cacheMeta->isObject()) {606 if (!cacheMeta.isObject()) {
607 cacheMeta = _engine->newObject();607 cacheMeta = _engine->newObject();
608 cacheMeta->setProperty("id", "Script.require.cacheMeta");608 cacheMeta.setProperty("id", "Script.require.cacheMeta");
609 cacheMeta->setProperty("type", "cacheMeta");609 cacheMeta.setProperty("type", "cacheMeta");
610 jsRequire->setData(cacheMeta);610 jsRequire.setData(cacheMeta);
611 }611 }
612 cache->setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), ScriptValue::SkipInEnumeration);612 cache.setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), ScriptValue::SkipInEnumeration);
613#if DEBUG_JS_MODULES613#if DEBUG_JS_MODULES
614 cache.setProperty("__meta__", cacheMeta, READONLY_HIDDEN_PROP_FLAGS);614 cache.setProperty("__meta__", cacheMeta, READONLY_HIDDEN_PROP_FLAGS);
615#endif615#endif
616 jsRequire->setProperty("cache", cache, READONLY_PROP_FLAGS);616 jsRequire.setProperty("cache", cache, READONLY_PROP_FLAGS);
617}617}
618 618
619void ScriptManager::init() {619void ScriptManager::init() {
@@ -638,11 +638,11 @@ void ScriptManager::init() { · 18 unmodified lines
638 scriptRegisterSequenceMetaType<QVector<glm::quat>>(scriptEngine);638 scriptRegisterSequenceMetaType<QVector<glm::quat>>(scriptEngine);
639 scriptRegisterSequenceMetaType<QVector<QString>>(scriptEngine);639 scriptRegisterSequenceMetaType<QVector<QString>>(scriptEngine);
640 640
641 ScriptValuePointer xmlHttpRequestConstructorValue = scriptEngine->newFunction(XMLHttpRequestClass::constructor);641 ScriptValue xmlHttpRequestConstructorValue = scriptEngine->newFunction(XMLHttpRequestClass::constructor);
642 scriptEngine->globalObject()->setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue);642 scriptEngine->globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue);
643 643
644 ScriptValuePointer webSocketConstructorValue = scriptEngine->newFunction(WebSocketClass::constructor);644 ScriptValue webSocketConstructorValue = scriptEngine->newFunction(WebSocketClass::constructor);
645 scriptEngine->globalObject()->setProperty("WebSocket", webSocketConstructorValue);645 scriptEngine->globalObject().setProperty("WebSocket", webSocketConstructorValue);
646 646
647 /*@jsdoc647 /*@jsdoc
648 * Prints a message to the program log and emits {@link Script.printedMessage}.648 * Prints a message to the program log and emits {@link Script.printedMessage}.
@@ -651,7 +651,7 @@ void ScriptManager::init() { · 2 unmodified lines
651 * @function print651 * @function print
652 * @param {...*} [message] - The message values to print.652 * @param {...*} [message] - The message values to print.
653 */653 */
654 scriptEngine->globalObject()->setProperty("print", scriptEngine->newFunction(debugPrint));654 scriptEngine->globalObject().setProperty("print", scriptEngine->newFunction(debugPrint));
655 655
656 scriptRegisterMetaType(scriptEngine, animationDetailsToScriptValue, animationDetailsFromScriptValue);656 scriptRegisterMetaType(scriptEngine, animationDetailsToScriptValue, animationDetailsFromScriptValue);
657 scriptRegisterMetaType(scriptEngine, webSocketToScriptValue, webSocketFromScriptValue);657 scriptRegisterMetaType(scriptEngine, webSocketToScriptValue, webSocketFromScriptValue);
@@ -665,10 +665,10 @@ void ScriptManager::init() { · 7 unmodified lines
665 665
666 {666 {
667 // set up Script.require.resolve and Script.require.cache667 // set up Script.require.resolve and Script.require.cache
668 auto Script = scriptEngine->globalObject()->property("Script");668 auto Script = scriptEngine->globalObject().property("Script");
669 auto require = Script->property("require");669 auto require = Script.property("require");
670 auto resolve = Script->property("_requireResolve");670 auto resolve = Script.property("_requireResolve");
671 require->setProperty("resolve", resolve, READONLY_PROP_FLAGS);671 require.setProperty("resolve", resolve, READONLY_PROP_FLAGS);
672 resetModuleCache();672 resetModuleCache();
673 }673 }
674 674
@@ -697,12 +697,12 @@ void ScriptManager::init() { · 22 unmodified lines
697 697
698 // Scriptable cache access698 // Scriptable cache access
699 auto resourcePrototype = createScriptableResourcePrototype(qSharedPointerCast<ScriptManager>(sharedFromThis()));699 auto resourcePrototype = createScriptableResourcePrototype(qSharedPointerCast<ScriptManager>(sharedFromThis()));
700 scriptEngine->globalObject()->setProperty("Resource", resourcePrototype);700 scriptEngine->globalObject().setProperty("Resource", resourcePrototype);
701 scriptEngine->setDefaultPrototype(qMetaTypeId<ScriptableResource*>(), resourcePrototype);701 scriptEngine->setDefaultPrototype(qMetaTypeId<ScriptableResource*>(), resourcePrototype);
702 scriptRegisterMetaType(scriptEngine, scriptableResourceToScriptValue, scriptableResourceFromScriptValue);702 scriptRegisterMetaType(scriptEngine, scriptableResourceToScriptValue, scriptableResourceFromScriptValue);
703 703
704 // constants704 // constants
705 scriptEngine->globalObject()->setProperty("TREE_SCALE", scriptEngine->newValue(TREE_SCALE));705 scriptEngine->globalObject().setProperty("TREE_SCALE", scriptEngine->newValue(TREE_SCALE));
706 706
707 scriptEngine->registerGlobalObject("Assets", _assetScriptingInterface);707 scriptEngine->registerGlobalObject("Assets", _assetScriptingInterface);
708 scriptEngine->registerGlobalObject("Resources", DependencyManager::get<ResourceScriptingInterface>().data());708 scriptEngine->registerGlobalObject("Resources", DependencyManager::get<ResourceScriptingInterface>().data());
@@ -718,14 +718,14 @@ void ScriptManager::init() { · 9 unmodified lines
718 scriptEngine->registerGlobalObject("StackTest", new StackTestScriptingInterface(this));718 scriptEngine->registerGlobalObject("StackTest", new StackTestScriptingInterface(this));
719#endif719#endif
720 720
721 scriptEngine->globalObject()->setProperty("KALILA", "isWaifu");721 scriptEngine->globalObject().setProperty("KALILA", "isWaifu");
722 scriptEngine->globalObject()->setProperty("Kute", scriptEngine->newFunction([](ScriptContext* context, ScriptEngine* engine) -> ScriptValuePointer {722 scriptEngine->globalObject().setProperty("Kute", scriptEngine->newFunction([](ScriptContext* context, ScriptEngine* engine) -> ScriptValue {
723 return context->engine()->newValue(context->argument(0)->toString().toLower() == "kalila" ? true : false);723 return context->engine()->newValue(context->argument(0).toString().toLower() == "kalila" ? true : false);
724 }));724 }));
725}725}
726 726
727// Unregister the handlers for this eventName and entityID.727// Unregister the handlers for this eventName and entityID.
728void ScriptManager::removeEventHandler(const EntityItemID& entityID, const QString& eventName, ScriptValuePointer handler) {728void ScriptManager::removeEventHandler(const EntityItemID& entityID, const QString& eventName, const ScriptValue& handler) {
729 if (QThread::currentThread() != thread()) {729 if (QThread::currentThread() != thread()) {
730#ifdef THREAD_DEBUGGING730#ifdef THREAD_DEBUGGING
731 qCDebug(scriptengine) << "*** WARNING *** ScriptManager::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "731 qCDebug(scriptengine) << "*** WARNING *** ScriptManager::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
@@ -734,7 +734,7 @@ void ScriptManager::removeEventHandler(const EntityItemID& entityID, const QStri · 2 unmodified lines
734 QMetaObject::invokeMethod(this, "removeEventHandler",734 QMetaObject::invokeMethod(this, "removeEventHandler",
735 Q_ARG(const EntityItemID&, entityID),735 Q_ARG(const EntityItemID&, entityID),
736 Q_ARG(const QString&, eventName),736 Q_ARG(const QString&, eventName),
737 Q_ARG(ScriptValuePointer, handler));737 Q_ARG(const ScriptValue&, handler));
738 return;738 return;
739 }739 }
740#ifdef THREAD_DEBUGGING740#ifdef THREAD_DEBUGGING
@@ -746,9 +746,9 @@ void ScriptManager::removeEventHandler(const EntityItemID& entityID, const QStri · 5 unmodified lines
746 }746 }
747 RegisteredEventHandlers& handlersOnEntity = _registeredHandlers[entityID];747 RegisteredEventHandlers& handlersOnEntity = _registeredHandlers[entityID];
748 CallbackList& handlersForEvent = handlersOnEntity[eventName];748 CallbackList& handlersForEvent = handlersOnEntity[eventName];
749 // ScriptValuePointer does not have operator==(), so we can't use QList::removeOne and friends. So iterate.749 // ScriptValue does not have operator==(), so we can't use QList::removeOne and friends. So iterate.
750 for (int i = 0; i < handlersForEvent.count(); ++i) {750 for (int i = 0; i < handlersForEvent.count(); ++i) {
751 if (handlersForEvent[i].function->equals(handler)) {751 if (handlersForEvent[i].function.equals(handler)) {
752 handlersForEvent.removeAt(i);752 handlersForEvent.removeAt(i);
753 return; // Design choice: since comparison is relatively expensive, just remove the first matching handler.753 return; // Design choice: since comparison is relatively expensive, just remove the first matching handler.
754 }754 }
@@ -771,7 +771,7 @@ void ScriptManager::removeAllEventHandlers(const EntityItemID& entityID) { · 16 unmodified lines
771}771}
772 772
773// Register the handler.773// Register the handler.
774void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString& eventName, ScriptValuePointer handler) {774void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString& eventName, const ScriptValue& handler) {
775 if (QThread::currentThread() != thread()) {775 if (QThread::currentThread() != thread()) {
776#ifdef THREAD_DEBUGGING776#ifdef THREAD_DEBUGGING
777 qCDebug(scriptengine) << "*** WARNING *** ScriptManager::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "777 qCDebug(scriptengine) << "*** WARNING *** ScriptManager::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
@@ -781,7 +781,7 @@ void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString& · 3 unmodified lines
781 QMetaObject::invokeMethod(this, "addEventHandler",781 QMetaObject::invokeMethod(this, "addEventHandler",
782 Q_ARG(const EntityItemID&, entityID),782 Q_ARG(const EntityItemID&, entityID),
783 Q_ARG(const QString&, eventName),783 Q_ARG(const QString&, eventName),
784 Q_ARG(ScriptValuePointer, handler));784 Q_ARG(const ScriptValue&, handler));
785 return;785 return;
786 }786 }
787#ifdef THREAD_DEBUGGING787#ifdef THREAD_DEBUGGING
@@ -951,7 +951,7 @@ void ScriptManager::run() { · 163 unmodified lines
951 // only clear exceptions if we are not in the middle of evaluating951 // only clear exceptions if we are not in the middle of evaluating
952 if (!_engine->isEvaluating() && _engine->hasUncaughtException()) {952 if (!_engine->isEvaluating() && _engine->hasUncaughtException()) {
953 qCWarning(scriptengine) << __FUNCTION__ << "---------- UNCAUGHT EXCEPTION --------";953 qCWarning(scriptengine) << __FUNCTION__ << "---------- UNCAUGHT EXCEPTION --------";
954 qCWarning(scriptengine) << "runInThread" << _engine->uncaughtException()->toString();954 qCWarning(scriptengine) << "runInThread" << _engine->uncaughtException().toString();
955 emit unhandledException(_engine->cloneUncaughtException(__FUNCTION__));955 emit unhandledException(_engine->cloneUncaughtException(__FUNCTION__));
956 _engine->clearExceptions();956 _engine->clearExceptions();
957 }957 }
@@ -1037,7 +1037,7 @@ void ScriptManager::timerFired() { · 79 unmodified lines
1037 }1037 }
1038 1038
1039 // call the associated JS function, if it exists1039 // call the associated JS function, if it exists
1040 if (timerData.function->isValid()) {1040 if (timerData.function.isValid()) {
1041 PROFILE_RANGE(script, __FUNCTION__);1041 PROFILE_RANGE(script, __FUNCTION__);
1042 auto preTimer = p_high_resolution_clock::now();1042 auto preTimer = p_high_resolution_clock::now();
1043 callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, ScriptValueList());1043 callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, ScriptValueList());
@@ -1045,11 +1045,11 @@ void ScriptManager::timerFired() { · 1 unmodified line
1045 auto elapsed = (postTimer - preTimer);1045 auto elapsed = (postTimer - preTimer);
1046 _totalTimerExecution += std::chrono::duration_cast<std::chrono::microseconds>(elapsed);1046 _totalTimerExecution += std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
1047 } else {1047 } else {
1048 qCWarning(scriptengine) << "timerFired -- invalid function" << timerData.function->toVariant().toString();1048 qCWarning(scriptengine) << "timerFired -- invalid function" << timerData.function.toVariant().toString();
1049 }1049 }
1050}1050}
1051 1051
1052QObject* ScriptManager::setupTimerWithInterval(const ScriptValuePointer& function, int intervalMS, bool isSingleShot) {1052QObject* ScriptManager::setupTimerWithInterval(const ScriptValue& function, int intervalMS, bool isSingleShot) {
1053 // create the timer, add it to the map, and start it1053 // create the timer, add it to the map, and start it
1054 QTimer* newTimer = new QTimer(this);1054 QTimer* newTimer = new QTimer(this);
1055 newTimer->setSingleShot(isSingleShot);1055 newTimer->setSingleShot(isSingleShot);
@@ -1073,7 +1073,7 @@ QObject* ScriptManager::setupTimerWithInterval(const ScriptValuePointer& functio · 17 unmodified lines
1073 return newTimer;1073 return newTimer;
1074}1074}
1075 1075
1076QObject* ScriptManager::setInterval(const ScriptValuePointer& function, int intervalMS) {1076QObject* ScriptManager::setInterval(const ScriptValue& function, int intervalMS) {
1077 if (isStopped()) {1077 if (isStopped()) {
1078 scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename());1078 scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename());
1079 return NULL; // bail early1079 return NULL; // bail early
@@ -1082,7 +1082,7 @@ QObject* ScriptManager::setInterval(const ScriptValuePointer& function, int inte · 2 unmodified lines
1082 return setupTimerWithInterval(function, intervalMS, false);1082 return setupTimerWithInterval(function, intervalMS, false);
1083}1083}
1084 1084
1085QObject* ScriptManager::setTimeout(const ScriptValuePointer& function, int timeoutMS) {1085QObject* ScriptManager::setTimeout(const ScriptValue& function, int timeoutMS) {
1086 if (isStopped()) {1086 if (isStopped()) {
1087 scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename());1087 scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename());
1088 return NULL; // bail early1088 return NULL; // bail early
@@ -1174,7 +1174,7 @@ QString ScriptManager::_requireResolve(const QString& moduleId, const QString& r · 85 unmodified lines
1174 }1174 }
1175 auto message = QString("Cannot find module '%1' (%2)").arg(displayId);1175 auto message = QString("Cannot find module '%1' (%2)").arg(displayId);
1176 1176
1177 auto throwResolveError = [&](const ScriptValuePointer& error) -> QString {1177 auto throwResolveError = [&](const ScriptValue& error) -> QString {
1178 _engine->raiseException(error);1178 _engine->raiseException(error);
1179 _engine->maybeEmitUncaughtException("require.resolve");1179 _engine->maybeEmitUncaughtException("require.resolve");
1180 return QString();1180 return QString();
@@ -1253,73 +1253,73 @@ QString ScriptManager::_requireResolve(const QString& moduleId, const QString& r · 72 unmodified lines
1253}1253}
1254 1254
1255// retrieves the current parent module from the JS scope chain1255// retrieves the current parent module from the JS scope chain
1256ScriptValuePointer ScriptManager::currentModule() {1256ScriptValue ScriptManager::currentModule() {
1257 if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {1257 if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
1258 return _engine->nullValue();1258 return _engine->nullValue();
1259 }1259 }
1260 auto jsRequire = _engine->globalObject()->property("Script")->property("require");1260 auto jsRequire = _engine->globalObject().property("Script").property("require");
1261 auto cache = jsRequire->property("cache");1261 auto cache = jsRequire.property("cache");
1262 ScriptValuePointer candidate;1262 ScriptValue candidate;
1263 ScriptContextPointer parentContext; // using this variable to maintain parent variable lifespan1263 ScriptContextPointer parentContext; // using this variable to maintain parent variable lifespan
1264 for (auto context = _engine->currentContext(); context && (!candidate || !candidate->isObject()); parentContext = context->parentContext(), context = parentContext.data()) {1264 for (auto context = _engine->currentContext(); context && !candidate.isObject(); parentContext = context->parentContext(), context = parentContext.data()) {
1265 auto contextInfo = context->functionContext();1265 auto contextInfo = context->functionContext();
1266 candidate = cache->property(contextInfo->fileName());1266 candidate = cache.property(contextInfo->fileName());
1267 }1267 }
1268 if (!candidate || !candidate->isObject()) {1268 if (!candidate.isObject()) {
1269 return ScriptValuePointer();1269 return ScriptValue();
1270 }1270 }
1271 return candidate;1271 return candidate;
1272}1272}
1273 1273
1274// replaces or adds "module" to "parent.children[]" array1274// replaces or adds "module" to "parent.children[]" array
1275// (for consistency with Node.js and userscript cache invalidation without "cache busters")1275// (for consistency with Node.js and userscript cache invalidation without "cache busters")
1276bool ScriptManager::registerModuleWithParent(const ScriptValuePointer& module, const ScriptValuePointer& parent) {1276bool ScriptManager::registerModuleWithParent(const ScriptValue& module, const ScriptValue& parent) {
1277 auto children = parent->property("children");1277 auto children = parent.property("children");
1278 if (children->isArray()) {1278 if (children.isArray()) {
1279 auto key = module->property("id");1279 auto key = module.property("id");
1280 auto length = children->property("length")->toInt32();1280 auto length = children.property("length").toInt32();
1281 for (int i = 0; i < length; i++) {1281 for (int i = 0; i < length; i++) {
1282 if (children->property(i)->property("id")->strictlyEquals(key)) {1282 if (children.property(i).property("id").strictlyEquals(key)) {
1283 qCDebug(scriptengine_module) << key->toString() << " updating parent.children[" << i << "] = module";1283 qCDebug(scriptengine_module) << key.toString() << " updating parent.children[" << i << "] = module";
1284 children->setProperty(i, module);1284 children.setProperty(i, module);
1285 return true;1285 return true;
1286 }1286 }
1287 }1287 }
1288 qCDebug(scriptengine_module) << key->toString() << " appending parent.children[" << length << "] = module";1288 qCDebug(scriptengine_module) << key.toString() << " appending parent.children[" << length << "] = module";
1289 children->setProperty(length, module);1289 children.setProperty(length, module);
1290 return true;1290 return true;
1291 } else if (parent->isValid()) {1291 } else if (parent.isValid()) {
1292 qCDebug(scriptengine_module) << "registerModuleWithParent -- unrecognized parent" << parent->toVariant().toString();1292 qCDebug(scriptengine_module) << "registerModuleWithParent -- unrecognized parent" << parent.toVariant().toString();
1293 }1293 }
1294 return false;1294 return false;
1295}1295}
1296 1296
1297// creates a new JS "module" Object with default metadata properties1297// creates a new JS "module" Object with default metadata properties
1298ScriptValuePointer ScriptManager::newModule(const QString& modulePath, const ScriptValuePointer& parent) {1298ScriptValue ScriptManager::newModule(const QString& modulePath, const ScriptValue& parent) {
1299 auto closure = _engine->newObject();1299 auto closure = _engine->newObject();
1300 auto exports = _engine->newObject();1300 auto exports = _engine->newObject();
1301 auto module = _engine->newObject();1301 auto module = _engine->newObject();
1302 qCDebug(scriptengine_module) << "newModule" << parent->property("filename")->toString();1302 qCDebug(scriptengine_module) << "newModule" << parent.property("filename").toString();
1303 1303
1304 closure->setProperty("module", module, READONLY_PROP_FLAGS);1304 closure.setProperty("module", module, READONLY_PROP_FLAGS);
1305 1305
1306 // note: this becomes the "exports" free variable, so should not be set read only1306 // note: this becomes the "exports" free variable, so should not be set read only
1307 closure->setProperty("exports", exports);1307 closure.setProperty("exports", exports);
1308 1308
1309 // make the closure available to module instantiation1309 // make the closure available to module instantiation
1310 module->setProperty("__closure__", closure, READONLY_HIDDEN_PROP_FLAGS);1310 module.setProperty("__closure__", closure, READONLY_HIDDEN_PROP_FLAGS);
1311 1311
1312 // for consistency with Node.js Module1312 // for consistency with Node.js Module
1313 module->setProperty("id", modulePath, READONLY_PROP_FLAGS);1313 module.setProperty("id", modulePath, READONLY_PROP_FLAGS);
1314 module->setProperty("filename", modulePath, READONLY_PROP_FLAGS);1314 module.setProperty("filename", modulePath, READONLY_PROP_FLAGS);
1315 module->setProperty("exports", exports); // not readonly1315 module.setProperty("exports", exports); // not readonly
1316 module->setProperty("loaded", false, READONLY_PROP_FLAGS);1316 module.setProperty("loaded", false, READONLY_PROP_FLAGS);
1317 module->setProperty("parent", parent, READONLY_PROP_FLAGS);1317 module.setProperty("parent", parent, READONLY_PROP_FLAGS);
1318 module->setProperty("children", _engine->newArray(), READONLY_PROP_FLAGS);1318 module.setProperty("children", _engine->newArray(), READONLY_PROP_FLAGS);
1319 1319
1320 // module.require is a bound version of require that always resolves relative to that module's path1320 // module.require is a bound version of require that always resolves relative to that module's path
1321 auto boundRequire = _engine->evaluate("(function(id) { return Script.require(Script.require.resolve(id, this.filename)); })", "(boundRequire)");1321 auto boundRequire = _engine->evaluate("(function(id) { return Script.require(Script.require.resolve(id, this.filename)); })", "(boundRequire)");
1322 module->setProperty("require", boundRequire, READONLY_PROP_FLAGS);1322 module.setProperty("require", boundRequire, READONLY_PROP_FLAGS);
1323 1323
1324 return module;1324 return module;
1325}1325}
@@ -1383,23 +1383,23 @@ QVariantMap ScriptManager::fetchModuleSource(const QString& modulePath, const bo · 57 unmodified lines
1383}1383}
1384 1384
1385// evaluate a pending module object using the fetched source code1385// evaluate a pending module object using the fetched source code
1386ScriptValuePointer ScriptManager::instantiateModule(const ScriptValuePointer& module, const QString& sourceCode) {1386ScriptValue ScriptManager::instantiateModule(const ScriptValue& module, const QString& sourceCode) {
1387 ScriptValuePointer result;1387 ScriptValue result;
1388 auto modulePath = module->property("filename")->toString();1388 auto modulePath = module.property("filename").toString();
1389 auto closure = module->property("__closure__");1389 auto closure = module.property("__closure__");
1390 1390
1391 qCDebug(scriptengine_module) << QString("require.instantiateModule: %1 / %2 bytes")1391 qCDebug(scriptengine_module) << QString("require.instantiateModule: %1 / %2 bytes")
1392 .arg(QUrl(modulePath).fileName()).arg(sourceCode.length());1392 .arg(QUrl(modulePath).fileName()).arg(sourceCode.length());
1393 1393
1394 if (module->property("content-type")->toString() == "application/json") {1394 if (module.property("content-type").toString() == "application/json") {
1395 qCDebug(scriptengine_module) << "... parsing as JSON";1395 qCDebug(scriptengine_module) << "... parsing as JSON";
1396 closure->setProperty("__json", sourceCode);1396 closure.setProperty("__json", sourceCode);
1397 result = _engine->evaluateInClosure(closure, _engine->newProgram( "module.exports = JSON.parse(__json)", modulePath ));1397 result = _engine->evaluateInClosure(closure, _engine->newProgram( "module.exports = JSON.parse(__json)", modulePath ));
1398 } else {1398 } else {
1399 // scoped vars for consistency with Node.js1399 // scoped vars for consistency with Node.js
1400 closure->setProperty("require", module->property("require"));1400 closure.setProperty("require", module.property("require"));
1401 closure->setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS);1401 closure.setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS);
1402 closure->setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS);1402 closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS);
1403 result = _engine->evaluateInClosure(closure, _engine->newProgram( sourceCode, modulePath ));1403 result = _engine->evaluateInClosure(closure, _engine->newProgram( sourceCode, modulePath ));
1404 }1404 }
1405 _engine->maybeEmitUncaughtException(__FUNCTION__);1405 _engine->maybeEmitUncaughtException(__FUNCTION__);
@@ -1407,19 +1407,19 @@ ScriptValuePointer ScriptManager::instantiateModule(const ScriptValuePointer& mo · 1 unmodified line
1407}1407}
1408 1408
1409// CommonJS/Node.js like require/module support1409// CommonJS/Node.js like require/module support
1410ScriptValuePointer ScriptManager::require(const QString& moduleId) {1410ScriptValue ScriptManager::require(const QString& moduleId) {
1411 qCDebug(scriptengine_module) << "ScriptManager::require(" << moduleId.left(MAX_DEBUG_VALUE_LENGTH) << ")";1411 qCDebug(scriptengine_module) << "ScriptManager::require(" << moduleId.left(MAX_DEBUG_VALUE_LENGTH) << ")";
1412 if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {1412 if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
1413 return _engine->nullValue();1413 return _engine->nullValue();
1414 }1414 }
1415 1415
1416 auto jsRequire = _engine->globalObject()->property("Script")->property("require");1416 auto jsRequire = _engine->globalObject().property("Script").property("require");
1417 auto cacheMeta = jsRequire.data();1417 auto cacheMeta = jsRequire.data();
1418 auto cache = jsRequire->property("cache");1418 auto cache = jsRequire.property("cache");
1419 auto parent = currentModule();1419 auto parent = currentModule();
1420 1420
1421 auto throwModuleError = [&](const QString& modulePath, const ScriptValuePointer& error) {1421 auto throwModuleError = [&](const QString& modulePath, const ScriptValue& error) {
1422 cache->setProperty(modulePath, _engine->nullValue());1422 cache.setProperty(modulePath, _engine->nullValue());
1423 if (!error.isNull()) {1423 if (!error.isNull()) {
1424#ifdef DEBUG_JS_MODULES1424#ifdef DEBUG_JS_MODULES
1425 qCWarning(scriptengine_module) << "throwing module error:" << error.toString() << modulePath << error.property("stack").toString();1425 qCWarning(scriptengine_module) << "throwing module error:" << error.toString() << modulePath << error.property("stack").toString();
@@ -1439,7 +1439,7 @@ ScriptValuePointer ScriptManager::require(const QString& moduleId) { · 13 unmodified lines
1439 }1439 }
1440 1440
1441 // check the resolved path against the cache1441 // check the resolved path against the cache
1442 auto module = cache->property(modulePath);1442 auto module = cache.property(modulePath);
1443 1443
1444 // modules get cached in `Script.require.cache` and (similar to Node.js) users can access it1444 // modules get cached in `Script.require.cache` and (similar to Node.js) users can access it
1445 // to inspect particular entries and invalidate them by deleting the key:1445 // to inspect particular entries and invalidate them by deleting the key:
@@ -1449,16 +1449,16 @@ ScriptValuePointer ScriptManager::require(const QString& moduleId) { · 3 unmodified lines
1449 Setting::Handle<bool> getCachebustSetting {"cachebustScriptRequire", false };1449 Setting::Handle<bool> getCachebustSetting {"cachebustScriptRequire", false };
1450 1450
1451 // cacheMeta is just used right now to tell deleted keys apart from undefined ones1451 // cacheMeta is just used right now to tell deleted keys apart from undefined ones
1452 bool invalidateCache = getCachebustSetting.get() || (module->isUndefined() && cacheMeta->property(moduleId)->isValid());1452 bool invalidateCache = getCachebustSetting.get() || (module.isUndefined() && cacheMeta.property(moduleId).isValid());
1453 1453
1454 // reset the cacheMeta record so invalidation won't apply next time, even if the module fails to load1454 // reset the cacheMeta record so invalidation won't apply next time, even if the module fails to load
1455 cacheMeta->setProperty(modulePath, ScriptValuePointer());1455 cacheMeta.setProperty(modulePath, ScriptValue());
1456 1456
1457 auto exports = module->property("exports");1457 auto exports = module.property("exports");
1458 if (!invalidateCache && exports->isObject()) {1458 if (!invalidateCache && exports.isObject()) {
1459 // we have found a cached module -- just need to possibly register it with current parent1459 // we have found a cached module -- just need to possibly register it with current parent
1460 qCDebug(scriptengine_module) << QString("require - using cached module for '%1' (loaded: %2)")1460 qCDebug(scriptengine_module) << QString("require - using cached module for '%1' (loaded: %2)")
1461 .arg(moduleId).arg(module->property("loaded")->toString());1461 .arg(moduleId).arg(module.property("loaded").toString());
1462 registerModuleWithParent(module, parent);1462 registerModuleWithParent(module, parent);
1463 _engine->maybeEmitUncaughtException("cached module");1463 _engine->maybeEmitUncaughtException("cached module");
1464 return exports;1464 return exports;
@@ -1469,7 +1469,7 @@ ScriptValuePointer ScriptManager::require(const QString& moduleId) { · 4 unmodified lines
1469 registerModuleWithParent(module, parent);1469 registerModuleWithParent(module, parent);
1470 1470
1471 // add it to the cache (this is done early so any cyclic dependencies pick up)1471 // add it to the cache (this is done early so any cyclic dependencies pick up)
1472 cache->setProperty(modulePath, module);1472 cache.setProperty(modulePath, module);
1473 1473
1474 // download the module source1474 // download the module source
1475 auto req = fetchModuleSource(modulePath, invalidateCache);1475 auto req = fetchModuleSource(modulePath, invalidateCache);
@@ -1487,36 +1487,36 @@ ScriptValuePointer ScriptManager::require(const QString& moduleId) { · 11 unmodified lines
1487 auto sourceCode = req["contents"].toString();1487 auto sourceCode = req["contents"].toString();
1488 1488
1489 if (QUrl(modulePath).fileName().endsWith(".json", Qt::CaseInsensitive)) {1489 if (QUrl(modulePath).fileName().endsWith(".json", Qt::CaseInsensitive)) {
1490 module->setProperty("content-type", "application/json");1490 module.setProperty("content-type", "application/json");
1491 } else {1491 } else {
1492 module->setProperty("content-type", "application/javascript");1492 module.setProperty("content-type", "application/javascript");
1493 }1493 }
1494 1494
1495 // evaluate the module1495 // evaluate the module
1496 auto result = instantiateModule(module, sourceCode);1496 auto result = instantiateModule(module, sourceCode);
1497 1497
1498 if (result->isError() && !result->strictlyEquals(module->property("exports"))) {1498 if (result.isError() && !result.strictlyEquals(module.property("exports"))) {
1499 qCWarning(scriptengine_module) << "-- result.isError --" << result->toString();1499 qCWarning(scriptengine_module) << "-- result.isError --" << result.toString();
1500 return throwModuleError(modulePath, result);1500 return throwModuleError(modulePath, result);
1501 }1501 }
1502 1502
1503 // mark as fully-loaded1503 // mark as fully-loaded
1504 module->setProperty("loaded", true, READONLY_PROP_FLAGS);1504 module.setProperty("loaded", true, READONLY_PROP_FLAGS);
1505 1505
1506 // set up a new reference point for detecting cache key deletion1506 // set up a new reference point for detecting cache key deletion
1507 cacheMeta->setProperty(modulePath, module);1507 cacheMeta.setProperty(modulePath, module);
1508 1508
1509 qCDebug(scriptengine_module) << "//ScriptManager::require(" << moduleId << ")";1509 qCDebug(scriptengine_module) << "//ScriptManager::require(" << moduleId << ")";
1510 1510
1511 _engine->maybeEmitUncaughtException(__FUNCTION__);1511 _engine->maybeEmitUncaughtException(__FUNCTION__);
1512 return module->property("exports");1512 return module.property("exports");
1513}1513}
1514 1514
1515// If a callback is specified, the included files will be loaded asynchronously and the callback will be called1515// If a callback is specified, the included files will be loaded asynchronously and the callback will be called
1516// when all of the files have finished loading.1516// when all of the files have finished loading.
1517// If no callback is specified, the included files will be loaded synchronously and will block execution until1517// If no callback is specified, the included files will be loaded synchronously and will block execution until
1518// all of the files have finished loading.1518// all of the files have finished loading.
1519void ScriptManager::include(const QStringList& includeFiles, ScriptValuePointer callback) {1519void ScriptManager::include(const QStringList& includeFiles, const ScriptValue& callback) {
1520 if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {1520 if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
1521 return;1521 return;
1522 }1522 }
@@ -1592,8 +1592,8 @@ void ScriptManager::include(const QStringList& includeFiles, ScriptValuePointer · 69 unmodified lines
1592 }1592 }
1593 _parentURL = parentURL;1593 _parentURL = parentURL;
1594 1594
1595 if (callback && callback->isFunction()) {1595 if (callback.isFunction()) {
1596 callWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, callback, ScriptValuePointer(), ScriptValueList());1596 callWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, callback, ScriptValue(), ScriptValueList());
1597 }1597 }
1598 1598
1599 loader->deleteLater();1599 loader->deleteLater();
@@ -1606,14 +1606,14 @@ void ScriptManager::include(const QStringList& includeFiles, ScriptValuePointer · 6 unmodified lines
1606 1606
1607 loader->start(processLevelMaxRetries);1607 loader->start(processLevelMaxRetries);
1608 1608
1609 if ((!callback || !callback->isFunction()) && !loader->isFinished()) {1609 if (!callback.isFunction() && !loader->isFinished()) {
1610 QEventLoop loop;1610 QEventLoop loop;
1611 QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit);1611 QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit);
1612 loop.exec();1612 loop.exec();
1613 }1613 }
1614}1614}
1615 1615
1616void ScriptManager::include(const QString& includeFile, ScriptValuePointer callback) {1616void ScriptManager::include(const QString& includeFile, const ScriptValue& callback) {
1617 if (isStopped()) {1617 if (isStopped()) {
1618 scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"1618 scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
1619 + includeFile + "parent script:" + getFilename());1619 + includeFile + "parent script:" + getFilename());
@@ -1654,7 +1654,7 @@ void ScriptManager::load(const QString& loadFile) { · 34 unmodified lines
1654}1654}
1655 1655
1656// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args1656// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
1657void ScriptManager::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, ScriptValueList eventHandlerArgs) {1657void ScriptManager::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, const ScriptValueList& eventHandlerArgs) {
1658 if (QThread::currentThread() != thread()) {1658 if (QThread::currentThread() != thread()) {
1659 qCDebug(scriptengine) << "*** ERROR *** ScriptManager::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";1659 qCDebug(scriptengine) << "*** ERROR *** ScriptManager::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
1660 assert(false);1660 assert(false);
@@ -1674,7 +1674,7 @@ void ScriptManager::forwardHandlerCall(const EntityItemID& entityID, const QStri · 13 unmodified lines
1674 // and the entity scripts may be for entities other than the one this is a handler for.1674 // and the entity scripts may be for entities other than the one this is a handler for.
1675 // Fortunately, the definingEntityIdentifier captured the entity script id (if any) when the handler was added.1675 // Fortunately, the definingEntityIdentifier captured the entity script id (if any) when the handler was added.
1676 CallbackData& handler = handlersForEvent[i];1676 CallbackData& handler = handlersForEvent[i];
1677 callWithEnvironment(handler.definingEntityIdentifier, handler.definingSandboxURL, handler.function, ScriptValuePointer(), eventHandlerArgs);1677 callWithEnvironment(handler.definingEntityIdentifier, handler.definingSandboxURL, handler.function, ScriptValue(), eventHandlerArgs);
1678 }1678 }
1679 }1679 }
1680}1680}
@@ -1921,13 +1921,13 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 240 unmodified lines
1921 1921
1922 // SYNTAX ERRORS1922 // SYNTAX ERRORS
1923 auto syntaxError = _engine->lintScript(contents, fileName);1923 auto syntaxError = _engine->lintScript(contents, fileName);
1924 if (syntaxError->isError()) {1924 if (syntaxError.isError()) {
1925 auto message = syntaxError->property("formatted")->toString();1925 auto message = syntaxError.property("formatted").toString();
1926 if (message.isEmpty()) {1926 if (message.isEmpty()) {
1927 message = syntaxError->toString();1927 message = syntaxError.toString();
1928 }1928 }
1929 setError(QString("Bad syntax (%1)").arg(message), EntityScriptStatus::ERROR_RUNNING_SCRIPT);1929 setError(QString("Bad syntax (%1)").arg(message), EntityScriptStatus::ERROR_RUNNING_SCRIPT);
1930 syntaxError->setProperty("detail", entityID.toString());1930 syntaxError.setProperty("detail", entityID.toString());
1931 emit unhandledException(syntaxError);1931 emit unhandledException(syntaxError);
1932 return;1932 return;
1933 }1933 }
@@ -1946,7 +1946,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 12 unmodified lines
1946 const int SANDBOX_TIMEOUT = 0.25 * MSECS_PER_SECOND;1946 const int SANDBOX_TIMEOUT = 0.25 * MSECS_PER_SECOND;
1947 ScriptEnginePointer sandbox = newScriptEngine();1947 ScriptEnginePointer sandbox = newScriptEngine();
1948 sandbox->setProcessEventsInterval(SANDBOX_TIMEOUT);1948 sandbox->setProcessEventsInterval(SANDBOX_TIMEOUT);
1949 ScriptValuePointer testConstructor, exception;1949 ScriptValue testConstructor, exception;
1950 if (atoi(getenv("UNSAFE_ENTITY_SCRIPTS") ? getenv("UNSAFE_ENTITY_SCRIPTS") : "0"))1950 if (atoi(getenv("UNSAFE_ENTITY_SCRIPTS") ? getenv("UNSAFE_ENTITY_SCRIPTS") : "0"))
1951 {1951 {
1952 QTimer timeout;1952 QTimer timeout;
@@ -1966,7 +1966,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 13 unmodified lines
1966 if (sandbox->hasUncaughtException()) {1966 if (sandbox->hasUncaughtException()) {
1967 exception = sandbox->cloneUncaughtException(QString("(preflight %1)").arg(entityID.toString()));1967 exception = sandbox->cloneUncaughtException(QString("(preflight %1)").arg(entityID.toString()));
1968 sandbox->clearExceptions();1968 sandbox->clearExceptions();
1969 } else if (testConstructor && testConstructor->isError()) {1969 } else if (testConstructor.isError()) {
1970 exception = testConstructor;1970 exception = testConstructor;
1971 }1971 }
1972 } else {1972 } else {
@@ -2041,7 +2041,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 68 unmodified lines
2041 if (sandbox->hasUncaughtException()) {2041 if (sandbox->hasUncaughtException()) {
2042 exception = sandbox->cloneUncaughtException(QString("(preflight %1)").arg(entityID.toString()));2042 exception = sandbox->cloneUncaughtException(QString("(preflight %1)").arg(entityID.toString()));
2043 sandbox->clearExceptions();2043 sandbox->clearExceptions();
2044 } else if (testConstructor && testConstructor->isError()) {2044 } else if (testConstructor.isError()) {
2045 exception = testConstructor;2045 exception = testConstructor;
2046 }2046 }
2047 }2047 }
@@ -2051,7 +2051,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 3 unmodified lines
2051 // exception = makeError("UNSAFE_ENTITY_SCRIPTS == 0");2051 // exception = makeError("UNSAFE_ENTITY_SCRIPTS == 0");
2052 }2052 }
2053 2053
2054 if (exception && exception->isError()) {2054 if (exception.isError()) {
2055 // create a local copy using makeError to decouple from the sandbox engine2055 // create a local copy using makeError to decouple from the sandbox engine
2056 exception = _engine->makeError(exception);2056 exception = _engine->makeError(exception);
2057 setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT);2057 setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT);
@@ -2060,12 +2060,12 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 2 unmodified lines
2060 }2060 }
2061 2061
2062 // CONSTRUCTOR VIABILITY2062 // CONSTRUCTOR VIABILITY
2063 if (!testConstructor || !testConstructor->isFunction()) {2063 if (!testConstructor.isFunction()) {
2064 QString testConstructorType = QString(testConstructor->toVariant().typeName());2064 QString testConstructorType = QString(testConstructor.toVariant().typeName());
2065 if (testConstructorType == "") {2065 if (testConstructorType == "") {
2066 testConstructorType = "empty";2066 testConstructorType = "empty";
2067 }2067 }
2068 QString testConstructorValue = testConstructor->toString();2068 QString testConstructorValue = testConstructor.toString();
2069 if (testConstructorValue.size() > MAX_DEBUG_VALUE_LENGTH) {2069 if (testConstructorValue.size() > MAX_DEBUG_VALUE_LENGTH) {
2070 testConstructorValue = testConstructorValue.mid(0, MAX_DEBUG_VALUE_LENGTH) + "...";2070 testConstructorValue = testConstructorValue.mid(0, MAX_DEBUG_VALUE_LENGTH) + "...";
2071 }2071 }
@@ -2073,8 +2073,8 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 1 unmodified line
2073 .arg(testConstructorType).arg(testConstructorValue);2073 .arg(testConstructorType).arg(testConstructorValue);
2074 2074
2075 auto err = _engine->makeError(_engine->newValue(message));2075 auto err = _engine->makeError(_engine->newValue(message));
2076 err->setProperty("fileName", scriptOrURL);2076 err.setProperty("fileName", scriptOrURL);
2077 err->setProperty("detail", "(constructor " + entityID.toString() + ")");2077 err.setProperty("detail", "(constructor " + entityID.toString() + ")");
2078 2078
2079 setError("Could not find constructor (" + testConstructorType + ")", EntityScriptStatus::ERROR_RUNNING_SCRIPT);2079 setError("Could not find constructor (" + testConstructorType + ")", EntityScriptStatus::ERROR_RUNNING_SCRIPT);
2080 emit unhandledException(err);2080 emit unhandledException(err);
@@ -2089,11 +2089,11 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 8 unmodified lines
2089 }2089 }
2090 2090
2091 // THE ACTUAL EVALUATION AND CONSTRUCTION2091 // THE ACTUAL EVALUATION AND CONSTRUCTION
2092 ScriptValuePointer entityScriptConstructor, entityScriptObject;2092 ScriptValue entityScriptConstructor, entityScriptObject;
2093 QUrl sandboxURL = currentSandboxURL.isEmpty() ? scriptOrURL : currentSandboxURL;2093 QUrl sandboxURL = currentSandboxURL.isEmpty() ? scriptOrURL : currentSandboxURL;
2094 auto initialization = [&]{2094 auto initialization = [&]{
2095 entityScriptConstructor = _engine->evaluate(contents, fileName);2095 entityScriptConstructor = _engine->evaluate(contents, fileName);
2096 entityScriptObject = entityScriptConstructor->construct();2096 entityScriptObject = entityScriptConstructor.construct();
2097 2097
2098 if (_engine->hasUncaughtException()) {2098 if (_engine->hasUncaughtException()) {
2099 entityScriptObject = _engine->cloneUncaughtException("(construct " + entityID.toString() + ")");2099 entityScriptObject = _engine->cloneUncaughtException("(construct " + entityID.toString() + ")");
@@ -2103,7 +2103,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 3 unmodified lines
2103 2103
2104 doWithEnvironment(entityID, sandboxURL, initialization);2104 doWithEnvironment(entityID, sandboxURL, initialization);
2105 2105
2106 if (entityScriptObject && entityScriptObject->isError()) {2106 if (entityScriptObject.isError()) {
2107 auto exception = entityScriptObject;2107 auto exception = entityScriptObject;
2108 setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT);2108 setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT);
2109 emit unhandledException(exception);2109 emit unhandledException(exception);
@@ -2266,7 +2266,7 @@ void ScriptManager::doWithEnvironment(const EntityItemID& entityID, const QUrl& · 156 unmodified lines
2266 currentSandboxURL = sandboxURL;2266 currentSandboxURL = sandboxURL;
2267 2267
2268#if DEBUG_CURRENT_ENTITY2268#if DEBUG_CURRENT_ENTITY
2269 ScriptValuePointer oldData = this->globalObject().property("debugEntityID");2269 ScriptValue oldData = this->globalObject().property("debugEntityID");
2270 this->globalObject().setProperty("debugEntityID", entityID.toScriptValue(this)); // Make the entityID available to javascript as a global.2270 this->globalObject().setProperty("debugEntityID", entityID.toScriptValue(this)); // Make the entityID available to javascript as a global.
2271 operation();2271 operation();
2272 this->globalObject().setProperty("debugEntityID", oldData);2272 this->globalObject().setProperty("debugEntityID", oldData);
@@ -2278,9 +2278,9 @@ void ScriptManager::doWithEnvironment(const EntityItemID& entityID, const QUrl& · 5 unmodified lines
2278 currentSandboxURL = oldSandboxURL;2278 currentSandboxURL = oldSandboxURL;
2279}2279}
2280 2280
2281void ScriptManager::callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, ScriptValuePointer function, ScriptValuePointer thisObject, ScriptValueList args) {2281void ScriptManager::callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, const ScriptValue& function, const ScriptValue& thisObject, const ScriptValueList& args) {
2282 auto operation = [&]() {2282 auto operation = [&]() {
2283 function->call(thisObject, args);2283 function.call(thisObject, args);
2284 };2284 };
2285 doWithEnvironment(entityID, sandboxURL, operation);2285 doWithEnvironment(entityID, sandboxURL, operation);
2286}2286}
@@ -2313,7 +2313,7 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 26 unmodified lines
2313 QWriteLocker locker { &_entityScriptsLock };2313 QWriteLocker locker { &_entityScriptsLock };
2314 details = _entityScripts[entityID];2314 details = _entityScripts[entityID];
2315 }2315 }
2316 ScriptValuePointer entityScript = details.scriptObject; // previously loaded2316 ScriptValue entityScript = details.scriptObject; // previously loaded
2317 2317
2318 // If this is a remote call, we need to check to see if the function is remotely callable2318 // If this is a remote call, we need to check to see if the function is remotely callable
2319 // we do this by checking for the existance of the 'remotelyCallable' property on the2319 // we do this by checking for the existance of the 'remotelyCallable' property on the
@@ -2323,11 +2323,11 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 3 unmodified lines
2323 if (remoteCallerID == QUuid()) {2323 if (remoteCallerID == QUuid()) {
2324 callAllowed = true;2324 callAllowed = true;
2325 } else {2325 } else {
2326 if (entityScript->property("remotelyCallable")->isArray()) {2326 if (entityScript.property("remotelyCallable").isArray()) {
2327 auto callables = entityScript->property("remotelyCallable");2327 auto callables = entityScript.property("remotelyCallable");
2328 auto callableCount = callables->property("length")->toInteger();2328 auto callableCount = callables.property("length").toInteger();
2329 for (int i = 0; i < callableCount; i++) {2329 for (int i = 0; i < callableCount; i++) {
2330 auto callable = callables->property(i)->toString();2330 auto callable = callables.property(i).toString();
2331 if (callable == methodName) {2331 if (callable == methodName) {
2332 callAllowed = true;2332 callAllowed = true;
2333 break;2333 break;
@@ -2339,17 +2339,17 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 5 unmodified lines
2339 }2339 }
2340 }2340 }
2341 2341
2342 if (callAllowed && entityScript->property(methodName)->isFunction()) {2342 if (callAllowed && entityScript.property(methodName).isFunction()) {
2343 auto scriptEngine = engine().data();2343 auto scriptEngine = engine().data();
2344 2344
2345 ScriptValueList args;2345 ScriptValueList args;
2346 args << EntityItemIDtoScriptValue(scriptEngine, entityID);2346 args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2347 args << scriptValueFromSequence(scriptEngine, params);2347 args << scriptValueFromSequence(scriptEngine, params);
2348 2348
2349 ScriptValuePointer oldData = scriptEngine->globalObject()->property("Script")->property("remoteCallerID");2349 ScriptValue oldData = scriptEngine->globalObject().property("Script").property("remoteCallerID");
2350 scriptEngine->globalObject()->property("Script")->setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global.2350 scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global.
2351 callWithEnvironment(entityID, details.definingSandboxURL, entityScript->property(methodName), entityScript, args);2351 callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
2352 scriptEngine->globalObject()->property("Script")->setProperty("remoteCallerID", oldData);2352 scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", oldData);
2353 }2353 }
2354 }2354 }
2355}2355}
@@ -2381,14 +2381,14 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 25 unmodified lines
2381 QWriteLocker locker { &_entityScriptsLock };2381 QWriteLocker locker { &_entityScriptsLock };
2382 details = _entityScripts[entityID];2382 details = _entityScripts[entityID];
2383 }2383 }
2384 ScriptValuePointer entityScript = details.scriptObject; // previously loaded2384 ScriptValue entityScript = details.scriptObject; // previously loaded
2385 if (entityScript->property(methodName)->isFunction()) {2385 if (entityScript.property(methodName).isFunction()) {
2386 auto scriptEngine = engine().data();2386 auto scriptEngine = engine().data();
2387 2387
2388 ScriptValueList args;2388 ScriptValueList args;
2389 args << EntityItemIDtoScriptValue(scriptEngine, entityID);2389 args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2390 args << event.toScriptValue(scriptEngine);2390 args << event.toScriptValue(scriptEngine);
2391 callWithEnvironment(entityID, details.definingSandboxURL, entityScript->property(methodName), entityScript, args);2391 callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
2392 }2392 }
2393 }2393 }
2394}2394}
@@ -2421,15 +2421,15 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 26 unmodified lines
2421 QWriteLocker locker { &_entityScriptsLock };2421 QWriteLocker locker { &_entityScriptsLock };
2422 details = _entityScripts[entityID];2422 details = _entityScripts[entityID];
2423 }2423 }
2424 ScriptValuePointer entityScript = details.scriptObject; // previously loaded2424 ScriptValue entityScript = details.scriptObject; // previously loaded
2425 if (entityScript->property(methodName)->isFunction()) {2425 if (entityScript.property(methodName).isFunction()) {
2426 auto scriptEngine = engine().data();2426 auto scriptEngine = engine().data();
2427 2427
2428 ScriptValueList args;2428 ScriptValueList args;
2429 args << EntityItemIDtoScriptValue(scriptEngine, entityID);2429 args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2430 args << EntityItemIDtoScriptValue(scriptEngine, otherID);2430 args << EntityItemIDtoScriptValue(scriptEngine, otherID);
2431 args << collisionToScriptValue(scriptEngine, collision);2431 args << collisionToScriptValue(scriptEngine, collision);
2432 callWithEnvironment(entityID, details.definingSandboxURL, entityScript->property(methodName), entityScript, args);2432 callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
2433 }2433 }
2434 }2434 }
2435}2435}
@@ -2438,28 +2438,28 @@ QString ScriptManager::getExternalPath(ExternalResource::Bucket bucket, const QS · 2 unmodified lines
2438 return ExternalResource::getInstance()->getUrl(bucket, path);2438 return ExternalResource::getInstance()->getUrl(bucket, path);
2439}2439}
2440 2440
2441QString ScriptManager::formatException(const ScriptValuePointer& exception, bool includeExtendedDetails) {2441QString ScriptManager::formatException(const ScriptValue& exception, bool includeExtendedDetails) {
2442 if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {2442 if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
2443 return QString();2443 return QString();
2444 }2444 }
2445 QString note{ "UncaughtException" };2445 QString note{ "UncaughtException" };
2446 QString result;2446 QString result;
2447 2447
2448 if (!exception->isObject()) {2448 if (!exception.isObject()) {
2449 return result;2449 return result;
2450 }2450 }
2451 const auto message = exception->toString();2451 const auto message = exception.toString();
2452 const auto fileName = exception->property("fileName")->toString();2452 const auto fileName = exception.property("fileName").toString();
2453 const auto lineNumber = exception->property("lineNumber")->toString();2453 const auto lineNumber = exception.property("lineNumber").toString();
2454 const auto stacktrace = exception->property("stack")->toString();2454 const auto stacktrace = exception.property("stack").toString();
2455 2455
2456 if (includeExtendedDetails) {2456 if (includeExtendedDetails) {
2457 // Display additional exception / troubleshooting hints that can be added via the custom Error .detail property2457 // Display additional exception / troubleshooting hints that can be added via the custom Error .detail property
2458 // Example difference:2458 // Example difference:
2459 // [UncaughtExceptions] Error: Can't find variable: foobar in atp:/myentity.js\n...2459 // [UncaughtExceptions] Error: Can't find variable: foobar in atp:/myentity.js\n...
2460 // [UncaughtException (construct {1eb5d3fa-23b1-411c-af83-163af7220e3f})] Error: Can't find variable: foobar in atp:/myentity.js\n...2460 // [UncaughtException (construct {1eb5d3fa-23b1-411c-af83-163af7220e3f})] Error: Can't find variable: foobar in atp:/myentity.js\n...
2461 if (exception->property("detail")->isValid()) {2461 if (exception.property("detail").isValid()) {
2462 note += " " + exception->property("detail")->toString();2462 note += " " + exception.property("detail").toString();
2463 }2463 }
2464 }2464 }
2465 2465
@@ -2470,6 +2470,6 @@ QString ScriptManager::formatException(const ScriptValuePointer& exception, bool · 4 unmodified lines
2470 return result;2470 return result;
2471}2471}
2472 2472
2473ScriptValuePointer ScriptManager::evaluate(const QString& program, const QString& fileName) {2473ScriptValue ScriptManager::evaluate(const QString& program, const QString& fileName) {
2474 return _engine->evaluate(program, fileName);2474 return _engine->evaluate(program, fileName);
2475}2475}