diff options
Diffstat (limited to 'src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp')
-rw-r--r-- | src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp b/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp new file mode 100644 index 00000000..4516c2f9 --- /dev/null +++ b/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2013 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "Qt3DSStateExecutionContext.h" +#include "Qt3DSStateExecutionTypes.h" +#include "Qt3DSStateInterpreter.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/StringConversionImpl.h" +#include "Qt3DSStateScriptContext.h" +#include "foundation/Utils.h" +#include "EASTL/string.h" + +using namespace qt3ds::state; + +namespace { +struct SExecContext : public IExecutionContext +{ + NVFoundationBase &m_Foundation; + NVScopedRefCounted<IStringTable> m_StringTable; + // Referencing this here would create circular references + IStateInterpreter *m_Interpreter; + IStateLogger *m_DebugLogger; + NVScopedRefCounted<IStateLogger> m_Logger; + NVScopedRefCounted<IScriptContext> m_ScriptContext; + bool m_Error; + QT3DSI32 mRefCount; + eastl::string m_DelayStr; + + SExecContext(NVFoundationBase &inFnd, IStringTable &inStrTable, IStateLogger &inLogger, + IScriptContext &inContext) + : m_Foundation(inFnd) + , m_StringTable(inStrTable) + , m_Interpreter(NULL) + , m_DebugLogger(NULL) + , m_Logger(inLogger) + , m_ScriptContext(inContext) + , m_Error(false) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + void SetInterpreter(IStateInterpreter &inInterpreter) override + { + m_Interpreter = &inInterpreter; + } + + void SetMachineDebugLogger(IStateLogger &inDebugLogger) override + { + m_DebugLogger = &inDebugLogger; + } + + void SignalError() + { + const char8_t *errorInfo = m_ScriptContext->GetErrorInfo(); + if (errorInfo && *errorInfo) { + m_Interpreter->QueueEvent("error.execution", false); + } + m_Error = true; + } + void ExecuteContent(SExecutableContent &content) + { + switch (content.m_Type) { + case ExecutableContentTypes::Send: { + SSend &theSend = *content.CastTo<SSend>(); + CRegisteredString theEvent; + if (!isTrivial(theSend.m_EventExpr)) { + SScriptExecutionResult theStr = + m_ScriptContext->ExecuteExpressionToString(theSend.m_EventExpr); + if (theStr.Valid()) + theEvent = m_StringTable->RegisterStr(theStr.Result()); + } else + theEvent = theSend.m_Event; + + QT3DSU64 theDelay(0); + m_DelayStr.clear(); + if (!isTrivial(theSend.m_DelayExpr)) { + SScriptExecutionResult theStr = + m_ScriptContext->ExecuteExpressionToString(theSend.m_DelayExpr); + if (theStr.Valid()) + m_DelayStr.assign(nonNull(theStr.Result())); + } else + m_DelayStr.assign(nonNull(theSend.m_Delay)); + if (m_DelayStr.size()) + theDelay = ParseTimeStrToMilliseconds(m_DelayStr.c_str()); + + if (theEvent.IsValid()) { + TEventPtr theEventPtr; + if (theSend.m_Children.empty() == false) { + IScriptEvent *theNewEvent = m_ScriptContext->CreateScriptEvent(theEvent); + theEventPtr = theNewEvent; + for (TExecutableContentList::iterator iter = theSend.m_Children.begin(), + end = theSend.m_Children.end(); + iter != end && m_Error == false; ++iter) { + if (iter->m_Type == ExecutableContentTypes::Param) { + SParam &theParam = static_cast<SParam &>(*iter); + if (theParam.m_Location.IsValid() == false) { + bool success = + theNewEvent->SetParam(theParam.m_Name, theParam.m_Expr); + if (!success) + SignalError(); + } else { + QT3DS_ASSERT(false); + } + } else if (iter->m_Type == ExecutableContentTypes::Content) { + SContent &theContent = static_cast<SContent &>(*iter); + if (!isTrivial(theContent.m_Expr)) { + bool success = theNewEvent->SetDataExpr(theContent.m_Expr); + if (!success) + SignalError(); + } else if (!isTrivial(theContent.m_ContentValue)) { + bool success = theNewEvent->SetDataStr(theContent.m_ContentValue); + if (!success) + SignalError(); + } + } else { + QT3DS_ASSERT(false); + } + } + } + if (m_Error == false) { + bool isExternal = true; + if (AreEqual("#_internal", theSend.m_Target)) + isExternal = false; + if (theEventPtr) + m_Interpreter->QueueEvent(theEventPtr, theDelay, theSend.m_Id, isExternal); + else + m_Interpreter->QueueEvent(theEvent, theDelay, theSend.m_Id, isExternal); + } + } + } break; + case ExecutableContentTypes::Cancel: { + SCancel &theCancel = *content.CastTo<SCancel>(); + if (theCancel.m_Send) { + m_Interpreter->CancelEvent(theCancel.m_Send->m_Id); + } else if (!isTrivial(theCancel.m_IdExpression)) { + SScriptExecutionResult theStr = + m_ScriptContext->ExecuteExpressionToString(theCancel.m_IdExpression); + if (theStr.Valid()) { + const char8_t *theStrVal(theStr.Result()); + if (!isTrivial(theStrVal)) + m_Interpreter->CancelEvent(m_StringTable->RegisterStr(theStrVal)); + } + } + } break; + case ExecutableContentTypes::Raise: { + SRaise &theRaise = *content.CastTo<SRaise>(); + m_Interpreter->QueueEvent(theRaise.m_Event, false); + } break; + case ExecutableContentTypes::Log: { + SLog *theLog = content.CastTo<SLog>(); + SScriptExecutionResult str = + m_ScriptContext->ExecuteExpressionToString(theLog->m_Expression); + if (str.Valid()) { + m_Logger->Log(theLog->m_Label, str.Result()); + if (m_DebugLogger) + m_DebugLogger->Log(theLog->m_Label, str.Result()); + } else + SignalError(); + } break; + case ExecutableContentTypes::If: { + SIf &theIf = *content.CastTo<SIf>(); + Option<bool> theCondResult = m_ScriptContext->ExecuteCondition(theIf.m_Cond); + if (theCondResult.hasValue()) { + bool currentConditionResult = *theCondResult; + for (TExecutableContentList::iterator ifIter = theIf.m_Children.begin(), + ifEnd = theIf.m_Children.end(); + ifIter != ifEnd && m_Error == false; ++ifIter) { + if (currentConditionResult) { + switch (ifIter->m_Type) { + case ExecutableContentTypes::Else: + case ExecutableContentTypes::ElseIf: + return; + default: + ExecuteContent(*ifIter); + break; + } + } else { + switch (ifIter->m_Type) { + case ExecutableContentTypes::ElseIf: { + SElseIf &theElseIf = *ifIter->CastTo<SElseIf>(); + theCondResult = m_ScriptContext->ExecuteCondition(theElseIf.m_Cond); + if (theCondResult.hasValue()) + currentConditionResult = *theCondResult; + else { + SignalError(); + return; + } + } break; + case ExecutableContentTypes::Else: + currentConditionResult = true; + break; + // Ignore all content that isn't if or else if we shouldn't be currently + // executing it. + default: + break; + } + } + } + } else + SignalError(); + } break; + case ExecutableContentTypes::Foreach: { + SForeach &theItem = *content.CastTo<SForeach>(); + Option<bool> success; + for (success = m_ScriptContext->BeginForeach(theItem.m_Array, theItem.m_Item, + theItem.m_Index); + success.hasValue() && *success && m_Error == false; + success = m_ScriptContext->NextForeach(theItem.m_Item, theItem.m_Index)) { + ExecuteContent(theItem.m_Children); + } + if (m_Error) { + + } else if (success.hasValue() == false) + SignalError(); + } break; + // We shouldn't get top level else or else if statements, they can only be inside an if + // statement. + case ExecutableContentTypes::Else: + case ExecutableContentTypes::ElseIf: + QT3DS_ASSERT(false); + break; + + case ExecutableContentTypes::Assign: { + SAssign &theAssign = *content.CastTo<SAssign>(); + bool success = m_ScriptContext->Assign(theAssign.m_Location, theAssign.m_Expression); + if (!success) + SignalError(); + } break; + case ExecutableContentTypes::Script: { + SScript &theScript = *content.CastTo<SScript>(); + if (!isTrivial(theScript.m_Data)) + m_ScriptContext->ExecuteScript(theScript.m_Data); + } break; + default: + qCCritical(INTERNAL_ERROR, "Unimplemented executable content %s", + ExecutableContentTypes::ToString(content.m_Type)); + } + } + + void ExecuteContent(TExecutableContentList &inContent) + { + for (TExecutableContentList::iterator iter = inContent.begin(), end = inContent.end(); + iter != end && m_Error == false; ++iter) { + ExecuteContent(*iter); + } + } + + void Execute(STransition &inTransaction) override + { + m_Error = false; + ExecuteContent(inTransaction.m_ExecutableContent); + } + + // These functions take the node as well as the list so a context can cache a fast + // execution path if necessary. + void Execute(SStateNode & /*inNode*/, TOnEntryList &inList) override + { + for (TOnEntryList::iterator iter = inList.begin(), end = inList.end(); iter != end; + ++iter) { + m_Error = false; + ExecuteContent(iter->m_ExecutableContent); + } + } + + void Execute(SStateNode & /*inNode*/, TOnExitList &inList) override + { + for (TOnExitList::iterator iter = inList.begin(), end = inList.end(); iter != end; ++iter) { + m_Error = false; + ExecuteContent(iter->m_ExecutableContent); + } + } +}; +} + +QT3DSU64 IExecutionContext::ParseTimeStrToMilliseconds(const char8_t *timeStr) +{ + if (isTrivial(timeStr)) + return 0; + + char *endPtr; + double theData = strtod(timeStr, &endPtr); + if (!isTrivial(endPtr)) { + if (AreEqual(endPtr, "s")) { + theData *= 1000; + } else if (AreEqual(endPtr, "ms")) { + // empty intentional + } else + theData = 0; + } else + theData = 0; + if (theData < 0) + theData = 0.0; + return static_cast<QT3DSU64>(theData); +} + +IExecutionContext &IExecutionContext::Create(NVFoundationBase &inFoundation, + IStringTable &inStringTable, IStateLogger &inLogger, + IScriptContext &inScriptContext) +{ + return *QT3DS_NEW(inFoundation.getAllocator(), SExecContext)(inFoundation, inStringTable, inLogger, + inScriptContext); +} |