summaryrefslogtreecommitdiffstats
path: root/src/uscxml
diff options
context:
space:
mode:
authorStefan Radomski <github@mintwerk.de>2017-01-13 16:47:44 (GMT)
committerStefan Radomski <github@mintwerk.de>2017-01-13 16:47:44 (GMT)
commit4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235 (patch)
tree8c023473bb342780ddf51a893d18369f1319bb5c /src/uscxml
parent0aa0fe08dc308c94379c47d0bf9745e341cb4c81 (diff)
downloaduscxml-4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235.zip
uscxml-4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235.tar.gz
uscxml-4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235.tar.bz2
First support for serialization and some bug fixes for DOM per data.src
Diffstat (limited to 'src/uscxml')
-rw-r--r--src/uscxml/Interpreter.cpp31
-rw-r--r--src/uscxml/Interpreter.h15
-rw-r--r--src/uscxml/interpreter/BasicContentExecutor.cpp70
-rw-r--r--src/uscxml/interpreter/BasicContentExecutor.h2
-rw-r--r--src/uscxml/interpreter/BasicDelayedEventQueue.cpp49
-rw-r--r--src/uscxml/interpreter/BasicDelayedEventQueue.h3
-rw-r--r--src/uscxml/interpreter/BasicEventQueue.cpp19
-rw-r--r--src/uscxml/interpreter/BasicEventQueue.h2
-rw-r--r--src/uscxml/interpreter/ContentExecutor.cpp4
-rw-r--r--src/uscxml/interpreter/ContentExecutor.h2
-rw-r--r--src/uscxml/interpreter/ContentExecutorImpl.h2
-rw-r--r--src/uscxml/interpreter/EventQueue.cpp8
-rw-r--r--src/uscxml/interpreter/EventQueue.h4
-rw-r--r--src/uscxml/interpreter/EventQueueImpl.h6
-rw-r--r--src/uscxml/interpreter/FastMicroStep.cpp152
-rw-r--r--src/uscxml/interpreter/FastMicroStep.h3
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.cpp140
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.h39
-rw-r--r--src/uscxml/interpreter/InterpreterMonitor.h3
-rw-r--r--src/uscxml/interpreter/MicroStep.cpp8
-rw-r--r--src/uscxml/interpreter/MicroStep.h7
-rw-r--r--src/uscxml/interpreter/MicroStepImpl.h6
-rw-r--r--src/uscxml/messages/Data.cpp143
-rw-r--r--src/uscxml/messages/Data.h10
-rw-r--r--src/uscxml/messages/Event.cpp58
-rw-r--r--src/uscxml/messages/Event.h18
-rw-r--r--src/uscxml/plugins/Invoker.cpp9
-rw-r--r--src/uscxml/plugins/Invoker.h5
-rw-r--r--src/uscxml/plugins/InvokerImpl.h13
-rw-r--r--src/uscxml/plugins/datamodel/CMakeLists.txt2
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp18
-rw-r--r--src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp41
-rw-r--r--src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h7
-rw-r--r--src/uscxml/util/DOM.cpp2
34 files changed, 756 insertions, 145 deletions
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 1aa07df..d5f764d 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -187,10 +187,25 @@ void Interpreter::reset() {
return _impl->reset();
}
+void Interpreter::deserialize(const std::string& encodedState) {
+ return _impl->deserialize(encodedState);
+}
+
+std::string Interpreter::serialize() {
+ return _impl->serialize();
+}
+
InterpreterState Interpreter::step(size_t blockMs) {
return _impl->step(blockMs);
};
+void loadState(const std::string& encodedState);
+
+/**
+ * Save the interpreter's state.
+ */
+std::string saveState();
+
void Interpreter::cancel() {
return _impl->cancel();
}
@@ -215,6 +230,10 @@ void Interpreter::setActionLanguage(ActionLanguage actionLanguage) {
return _impl->setActionLanguage(actionLanguage);
}
+ActionLanguage Interpreter::getActionLanguage() {
+ return _impl->getActionLanguage();
+}
+
void Interpreter::setFactory(Factory* factory) {
return _impl->setFactory(factory);
}
@@ -235,9 +254,7 @@ std::list<InterpreterIssue> Interpreter::validate() {
return InterpreterIssue::forInterpreter(_impl.get());
}
-std::recursive_mutex StateTransitionMonitor::_mutex;
-
-#if 0
+#if 1
static void printNodeSet(const std::list<XERCESC_NS::DOMElement*> nodes) {
std::string seperator;
for (auto nIter = nodes.begin(); nIter != nodes.end(); nIter++) {
@@ -247,6 +264,8 @@ static void printNodeSet(const std::list<XERCESC_NS::DOMElement*> nodes) {
}
#endif
+std::recursive_mutex StateTransitionMonitor::_mutex;
+
void StateTransitionMonitor::beforeTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
std::cerr << "Transition: " << uscxml::DOMUtils::xPathForNode(transition) << std::endl;
@@ -255,7 +274,7 @@ void StateTransitionMonitor::beforeTakingTransition(Interpreter& interpreter, co
void StateTransitionMonitor::onStableConfiguration(Interpreter& interpreter) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
std::cerr << "Stable Config: { ";
-// printNodeSet(_interpreter.getConfiguration());
+ printNodeSet(interpreter.getConfiguration());
std::cerr << " }" << std::endl;
}
@@ -292,8 +311,8 @@ void StateTransitionMonitor::beforeEnteringState(Interpreter& interpreter, const
void StateTransitionMonitor::beforeMicroStep(Interpreter& interpreter) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
- std::cerr << "Config: {";
-// printNodeSet(_interpreter.getConfiguration());
+ std::cerr << "Microstep in config: {";
+ printNodeSet(interpreter.getConfiguration());
std::cerr << "}" << std::endl;
}
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index cf87fc4..c74736e 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -146,6 +146,16 @@ public:
void reset();
/**
+ * Deserialize the state for the interpreter from a string.
+ */
+ void deserialize(const std::string& encodedState);
+
+ /**
+ * Serialize the interpreter's state in a string.
+ */
+ std::string serialize();
+
+ /**
* Get all state elements that constitute the active configuration.
* @return A list of XML elements of the active states.
*/
@@ -182,6 +192,11 @@ public:
void setActionLanguage(ActionLanguage actionLanguage);
/**
+ * Return ActionLanguage with the instances actually used (i.e. those from the factory).
+ */
+ ActionLanguage getActionLanguage();
+
+ /**
* Provide a custom Factory to instantiate dynamic instances for this and invoked state-chart instances.
*/
void setFactory(Factory* factory);
diff --git a/src/uscxml/interpreter/BasicContentExecutor.cpp b/src/uscxml/interpreter/BasicContentExecutor.cpp
index 60a45e5..bbd6bca 100644
--- a/src/uscxml/interpreter/BasicContentExecutor.cpp
+++ b/src/uscxml/interpreter/BasicContentExecutor.cpp
@@ -438,9 +438,10 @@ void BasicContentExecutor::invoke(XERCESC_NS::DOMElement* element) {
// content
std::list<DOMElement*> contents = DOMUtils::filterChildElements(XML_PREFIX(element).str() + "content", element);
if (contents.size() > 0) {
-#if 1
+#if 0
invokeEvent.data.node = contents.front();
#else
+ // test530
Data d = elementAsData(contents.front());
if (d.type == Data::INTERPRETED && d.atom.size() > 0) {
// immediately evaluate!
@@ -558,7 +559,7 @@ void BasicContentExecutor::processParams(std::multimap<std::string, Data>& param
}
}
-Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) {
+Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element, bool asExpression) {
if (HAS_ATTR(element, "expr")) {
// return _callbacks->evalAsData(ATTR(element, "expr"));
#if 0
@@ -570,6 +571,8 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) {
return Data(ATTR(element, "expr"), Data::INTERPRETED);
}
#endif
+ if (asExpression) // test 453
+ return Data(ATTR(element, "expr"), Data::INTERPRETED);
return _callbacks->evalAsData(ATTR(element, "expr"));
}
@@ -587,6 +590,7 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) {
// make an attempt to parse as XML
try {
+#if 0
XERCESC_NS::XercesDOMParser parser;
parser.setValidationScheme(XERCESC_NS::XercesDOMParser::Val_Never);
parser.setDoNamespaces(true);
@@ -604,8 +608,46 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) {
XERCESC_NS::DOMDocument* doc = parser.adoptDocument();
d.adoptedDoc = std::shared_ptr<XERCESC_NS::DOMDocument>(doc);
d.node = doc->getDocumentElement();
-
return d;
+#else
+ std::unique_ptr<XERCESC_NS::XercesDOMParser> parser(new XERCESC_NS::XercesDOMParser());
+ parser->setValidationScheme(XERCESC_NS::XercesDOMParser::Val_Always);
+ parser->setDoNamespaces(true);
+ parser->useScanner(XERCESC_NS::XMLUni::fgWFXMLScanner);
+
+ std::unique_ptr<XERCESC_NS::ErrorHandler> errHandler(new XERCESC_NS::HandlerBase());
+ parser->setErrorHandler(errHandler.get());
+
+ try {
+ std::string tmp = url;
+ parser->parse(tmp.c_str());
+
+ XERCESC_NS::DOMNode* newNode = element->getOwnerDocument()->importNode(parser->getDocument()->getDocumentElement(), true);
+
+ // remove any old child elements
+ while(element->getFirstElementChild() != NULL) {
+ element->removeChild(element->getFirstElementChild());
+ }
+ // we need to save the DOM somewhere .. Data::adoptedDoc was not good enough
+ element->appendChild(newNode);
+
+ Data d;
+// d.adoptedDoc = std::shared_ptr<XERCESC_NS::DOMDocument>(parser->adoptDocument());
+ d.node = newNode;
+ return d;
+ }
+
+ catch (const XERCESC_NS::SAXParseException& toCatch) {
+ ERROR_PLATFORM_THROW(X(toCatch.getMessage()).str());
+ } catch (const XERCESC_NS::RuntimeException& toCatch) {
+ ERROR_PLATFORM_THROW(X(toCatch.getMessage()).str());
+ } catch (const XERCESC_NS::XMLException& toCatch) {
+ ERROR_PLATFORM_THROW(X(toCatch.getMessage()).str());
+ } catch (const XERCESC_NS::DOMException& toCatch) {
+ ERROR_PLATFORM_THROW(X(toCatch.getMessage()).str());
+ }
+
+#endif
} catch (...) {
// just ignore and return as an interpreted string below
@@ -622,10 +664,9 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) {
// local content in document
std::list<DOMNode*> elementChildren = DOMUtils::filterChildType(DOMNode::ELEMENT_NODE, element);
- if (elementChildren.size() == 1) {
- return Data(elementChildren.front());
- } else if (elementChildren.size() > 1) {
- return Data(element);
+ if (elementChildren.size() > 0) {
+ // always return parent element, even with a single child node
+ return Data(static_cast<DOMNode*>(element));
}
std::list<DOMNode*> textChildren = DOMUtils::filterChildType(DOMNode::TEXT_NODE, element);
@@ -634,14 +675,23 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) {
for (auto textIter = textChildren.begin(); textIter != textChildren.end(); textIter++) {
contentSS << X((*textIter)->getNodeValue());
}
-#if 0
+
+ // test294, test562
+ if (LOCALNAME(element) == "content") {
+ return Data(spaceNormalize(contentSS.str()), Data::VERBATIM);
+ }
+
+ if (asExpression) // not actually used, but likely expected
+ return Data(contentSS.str(), Data::INTERPRETED);
+
+ // test153
try {
Data d = _callbacks->getAsData(contentSS.str());
if (!d.empty())
return d;
} catch(...) {}
-#endif
- // test294, test562
+
+ // never actually occurs with the w3c tests
return Data(spaceNormalize(contentSS.str()), Data::VERBATIM);
}
}
diff --git a/src/uscxml/interpreter/BasicContentExecutor.h b/src/uscxml/interpreter/BasicContentExecutor.h
index db53eb2..032da6f 100644
--- a/src/uscxml/interpreter/BasicContentExecutor.h
+++ b/src/uscxml/interpreter/BasicContentExecutor.h
@@ -52,7 +52,7 @@ public:
virtual void uninvoke(XERCESC_NS::DOMElement* invoke);
virtual void raiseDoneEvent(XERCESC_NS::DOMElement* state, XERCESC_NS::DOMElement* doneData);
- virtual Data elementAsData(XERCESC_NS::DOMElement* element);
+ virtual Data elementAsData(XERCESC_NS::DOMElement* element, bool asExpression = false);
protected:
void processNameLists(std::map<std::string, Data>& nameMap, XERCESC_NS::DOMElement* element);
diff --git a/src/uscxml/interpreter/BasicDelayedEventQueue.cpp b/src/uscxml/interpreter/BasicDelayedEventQueue.cpp
index 49ed2f9..3c87ac0 100644
--- a/src/uscxml/interpreter/BasicDelayedEventQueue.cpp
+++ b/src/uscxml/interpreter/BasicDelayedEventQueue.cpp
@@ -170,4 +170,53 @@ void BasicDelayedEventQueue::reset() {
_queue.clear();
}
+Data BasicDelayedEventQueue::serialize() {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+
+ if (_isStarted) {
+ _isStarted = false;
+ event_base_loopbreak(_eventLoop);
+ }
+ if (_thread) {
+ _thread->join();
+ delete _thread;
+ _thread = NULL;
+ }
+
+ Data serialized;
+
+ for (auto event : _queue) {
+ struct callbackData cb = _callbackData[event.uuid];
+
+ struct timeval delay, now;
+ uint64_t delayMs = 0;
+ gettimeofday(&now, NULL);
+
+ evutil_timersub(&delay, &cb.due, &now);
+ if (delay.tv_sec > 0 || (delay.tv_sec == 0 && delay.tv_usec > 0)) {
+ delayMs = delay.tv_sec * 1000 + delay.tv_usec / (double)1000;
+ }
+
+ Data delayedEvent;
+ delayedEvent["event"] = event;
+ delayedEvent["delay"] = Data(delayMs, Data::INTERPRETED);
+
+ serialized["BasicDelayedEventQueue"].array.push_back(event);
+ }
+
+ start();
+ return serialized;
+}
+
+void BasicDelayedEventQueue::deserialize(const Data& data) {
+ if (data.hasKey("BasicDelayedEventQueue")) {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+ for (auto event : data["BasicDelayedEventQueue"].array) {
+ Event e = Event::fromData(event["event"]);
+ enqueueDelayed(e, strTo<size_t>(event["delay"]), e.uuid);
+ }
+ }
+
+}
+
}
diff --git a/src/uscxml/interpreter/BasicDelayedEventQueue.h b/src/uscxml/interpreter/BasicDelayedEventQueue.h
index df5b13f..dfcd002 100644
--- a/src/uscxml/interpreter/BasicDelayedEventQueue.h
+++ b/src/uscxml/interpreter/BasicDelayedEventQueue.h
@@ -52,6 +52,9 @@ public:
}
virtual void reset();
+ virtual Data serialize();
+ virtual void deserialize(const Data& data);
+
protected:
virtual std::shared_ptr<EventQueueImpl> create() {
ErrorEvent e("Cannot create a DelayedEventQueue without callbacks");
diff --git a/src/uscxml/interpreter/BasicEventQueue.cpp b/src/uscxml/interpreter/BasicEventQueue.cpp
index 519754e..bb7c78b 100644
--- a/src/uscxml/interpreter/BasicEventQueue.cpp
+++ b/src/uscxml/interpreter/BasicEventQueue.cpp
@@ -73,6 +73,25 @@ void BasicEventQueue::reset() {
_queue.clear();
}
+Data BasicEventQueue::serialize() {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+ Data serialized;
+
+ for (auto event : _queue) {
+ serialized["BasicEventQueue"].array.push_back(event);
+ }
+ return serialized;
+}
+
+void BasicEventQueue::deserialize(const Data& data) {
+ if (data.hasKey("BasicEventQueue")) {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+ for (auto event : data["BasicEventQueue"].array) {
+ _queue.push_back(Event::fromData(event));
+ }
+ }
+}
+
std::shared_ptr<EventQueueImpl> BasicEventQueue::create() {
return std::shared_ptr<EventQueueImpl>(new BasicEventQueue());
}
diff --git a/src/uscxml/interpreter/BasicEventQueue.h b/src/uscxml/interpreter/BasicEventQueue.h
index 927b9c4..db74825 100644
--- a/src/uscxml/interpreter/BasicEventQueue.h
+++ b/src/uscxml/interpreter/BasicEventQueue.h
@@ -42,6 +42,8 @@ public:
virtual Event dequeue(size_t blockMs);
virtual void enqueue(const Event& event);
virtual void reset();
+ virtual Data serialize();
+ virtual void deserialize(const Data& data);
protected:
std::list<Event> _queue;
diff --git a/src/uscxml/interpreter/ContentExecutor.cpp b/src/uscxml/interpreter/ContentExecutor.cpp
index ca9d877..de142a1 100644
--- a/src/uscxml/interpreter/ContentExecutor.cpp
+++ b/src/uscxml/interpreter/ContentExecutor.cpp
@@ -35,8 +35,8 @@ void ContentExecutor::uninvoke(XERCESC_NS::DOMElement* invoke) {
_impl->uninvoke(invoke);
}
-Data ContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) {
- return _impl->elementAsData(element);
+Data ContentExecutor::elementAsData(XERCESC_NS::DOMElement* element, bool asExpression) {
+ return _impl->elementAsData(element, asExpression);
}
void ContentExecutor::raiseDoneEvent(XERCESC_NS::DOMElement* state, XERCESC_NS::DOMElement* doneData) {
diff --git a/src/uscxml/interpreter/ContentExecutor.h b/src/uscxml/interpreter/ContentExecutor.h
index 1559c5c..7a0b125 100644
--- a/src/uscxml/interpreter/ContentExecutor.h
+++ b/src/uscxml/interpreter/ContentExecutor.h
@@ -46,7 +46,7 @@ public:
virtual void process(XERCESC_NS::DOMElement* block, const X& xmlPrefix);
virtual void invoke(XERCESC_NS::DOMElement* invoke);
virtual void uninvoke(XERCESC_NS::DOMElement* invoke);
- virtual Data elementAsData(XERCESC_NS::DOMElement* element);
+ virtual Data elementAsData(XERCESC_NS::DOMElement* element, bool asExpression = false);
virtual void raiseDoneEvent(XERCESC_NS::DOMElement* state, XERCESC_NS::DOMElement* doneData);
virtual std::shared_ptr<ContentExecutorImpl> getImpl() const;
diff --git a/src/uscxml/interpreter/ContentExecutorImpl.h b/src/uscxml/interpreter/ContentExecutorImpl.h
index dfd4c5f..278cbb9 100644
--- a/src/uscxml/interpreter/ContentExecutorImpl.h
+++ b/src/uscxml/interpreter/ContentExecutorImpl.h
@@ -94,7 +94,7 @@ public:
virtual void uninvoke(XERCESC_NS::DOMElement* invoke) = 0;
virtual void raiseDoneEvent(XERCESC_NS::DOMElement* state, XERCESC_NS::DOMElement* doneData) = 0;
- virtual Data elementAsData(XERCESC_NS::DOMElement* element) = 0;
+ virtual Data elementAsData(XERCESC_NS::DOMElement* element, bool asExpression = false) = 0;
protected:
ContentExecutorCallbacks* _callbacks;
diff --git a/src/uscxml/interpreter/EventQueue.cpp b/src/uscxml/interpreter/EventQueue.cpp
index 9b345d5..fa9c4c1 100644
--- a/src/uscxml/interpreter/EventQueue.cpp
+++ b/src/uscxml/interpreter/EventQueue.cpp
@@ -42,6 +42,14 @@ void EventQueue::reset() {
return _impl->reset();
}
+Data EventQueue::serialize() {
+ return _impl->serialize();
+}
+
+void EventQueue::deserialize(const Data& data) {
+ return _impl->deserialize(data);
+}
+
std::shared_ptr<EventQueueImpl> EventQueue::getImplBase() {
return _impl;
}
diff --git a/src/uscxml/interpreter/EventQueue.h b/src/uscxml/interpreter/EventQueue.h
index 5ca1f52..76fdaa2 100644
--- a/src/uscxml/interpreter/EventQueue.h
+++ b/src/uscxml/interpreter/EventQueue.h
@@ -39,6 +39,10 @@ public:
virtual Event dequeue(size_t blockMs);
virtual void enqueue(const Event& event);
virtual void reset();
+
+ Data serialize();
+ void deserialize(const Data& data);
+
virtual std::shared_ptr<EventQueueImpl> getImplBase();
protected:
diff --git a/src/uscxml/interpreter/EventQueueImpl.h b/src/uscxml/interpreter/EventQueueImpl.h
index 2a33a75..50ac608 100644
--- a/src/uscxml/interpreter/EventQueueImpl.h
+++ b/src/uscxml/interpreter/EventQueueImpl.h
@@ -42,6 +42,8 @@ public:
virtual Event dequeue(size_t blockMs) = 0;
virtual void enqueue(const Event& event) = 0;
virtual void reset() = 0;
+ virtual Data serialize() = 0;
+ virtual void deserialize(const Data& data) = 0;
};
/**
@@ -63,6 +65,10 @@ public:
virtual void enqueueDelayed(const Event& event, size_t delayMs, const std::string& eventUUID) = 0;
virtual void cancelDelayed(const std::string& eventId) = 0;
virtual void cancelAllDelayed() = 0;
+
+ virtual Data serialize() = 0;
+ virtual void deserialize(const Data& data) = 0;
+
};
}
diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp
index 149ccb4..bf9749d 100644
--- a/src/uscxml/interpreter/FastMicroStep.cpp
+++ b/src/uscxml/interpreter/FastMicroStep.cpp
@@ -94,6 +94,45 @@ std::shared_ptr<MicroStepImpl> FastMicroStep::create(MicroStepCallbacks* callbac
return std::shared_ptr<MicroStepImpl>(new FastMicroStep(callbacks));
}
+void FastMicroStep::deserialize(const Data& encodedState) {
+ if (!encodedState.hasKey("configuration") ||
+ !encodedState.hasKey("invocations") ||
+ !encodedState.hasKey("histories") ||
+ !encodedState.hasKey("intializedData")) {
+ ERROR_PLATFORM_THROW("Data does not contain required fields for deserialization ");
+ }
+
+ _configuration = fromBase64(encodedState["configuration"].atom);
+ assert(_configuration.size() > 0 && _configuration.num_blocks() > 0);
+ _invocations = fromBase64(encodedState["invocations"].atom);
+ _history = fromBase64(encodedState["histories"].atom);
+ _initializedData = fromBase64(encodedState["intializedData"].atom);
+
+ for (size_t i = 0; i < USCXML_NUMBER_STATES; i++) {
+ if (BIT_HAS(i, _invocations) && USCXML_GET_STATE(i).invoke.size() > 0) {
+ for (auto invIter = USCXML_GET_STATE(i).invoke.begin(); invIter != USCXML_GET_STATE(i).invoke.end(); invIter++) {
+ try {
+ _callbacks->invoke(*invIter);
+ } catch (ErrorEvent e) {
+ LOG(_callbacks->getLogger(), USCXML_WARN) << e;
+ } catch (...) {
+ }
+ }
+ }
+ }
+
+ _flags |= USCXML_CTX_INITIALIZED;
+}
+
+Data FastMicroStep::serialize() {
+ Data encodedState;
+ encodedState["configuration"] = Data(toBase64(_configuration));
+ encodedState["invocations"] = Data(toBase64(_invocations));
+ encodedState["histories"] = Data(toBase64(_history));
+ encodedState["intializedData"] = Data(toBase64(_initializedData));
+ return encodedState;
+}
+
void FastMicroStep::resortStates(DOMElement* element, const X& xmlPrefix) {
/**
@@ -197,11 +236,11 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) {
_xmlPrefix = std::string(_xmlPrefix) + ":";
}
- resortStates(_scxml, _xmlPrefix);
+ resortStates(_scxml, _xmlPrefix);
#ifdef WITH_CACHE_FILES
bool withCache = !envVarIsTrue("USCXML_NOCACHE_FILES");
- Data& cache = _callbacks->getCache().compound["FastMicroStep"];
+ Data& cache = _callbacks->getCache().compound["FastMicroStep"];
#endif
/** -- All things states -- */
@@ -319,10 +358,17 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) {
// establish the states' completion
#ifdef WITH_CACHE_FILES
if (withCache && cachedState->compound.find("completion") != cachedState->compound.end()) {
- _states[i]->completion = fromBase64(cachedState->compound["completion"]);
- } else {
+ boost::dynamic_bitset<BITSET_BLOCKTYPE> completion = fromBase64(cachedState->compound["completion"]);
+ if (completion.size() != _states.size()) {
+ LOG(_callbacks->getLogger(), USCXML_WARN) << "State completion has wrong size: Cache corrupted";
+ } else {
+ _states[i]->completion = completion;
+ goto COMPLETION_STABLISHED;
+ }
+ }
#endif
- std::list<DOMElement*> completion = getCompletion(_states[i]->element);
+ {
+ std::list<DOMElement*> completion = getCompletion(_states[i]->element);
for (j = 0; j < _states.size(); j++) {
if (!completion.empty() && _states[j]->element == completion.front()) {
_states[i]->completion[j] = true;
@@ -332,12 +378,13 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) {
}
}
assert(completion.size() == 0);
-#ifdef WITH_CACHE_FILES
- if (withCache)
- cachedState->compound["completion"] = Data(toBase64(_states[i]->completion));
}
+#ifdef WITH_CACHE_FILES
+ if (withCache)
+ cachedState->compound["completion"] = Data(toBase64(_states[i]->completion));
+COMPLETION_STABLISHED:
#endif
- // this is set when establishing the completion
+ // this is set when establishing the completion
if (_states[i]->element->getUserData(X("hasHistoryChild")) == _states[i]) {
_states[i]->type |= USCXML_STATE_HAS_HISTORY;
}
@@ -400,16 +447,23 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) {
}
}
#endif
-
+
// establish the transitions' exit set
assert(_transitions[i]->element != NULL);
#ifdef WITH_CACHE_FILES
if (withCache && cachedTrans->compound.find("exitset") != cachedTrans->compound.end()) {
- _transitions[i]->exitSet = fromBase64(cachedTrans->compound["exitset"]);
- } else {
+ boost::dynamic_bitset<BITSET_BLOCKTYPE> exitSet = fromBase64(cachedTrans->compound["exitset"]);
+ if (exitSet.size() != _states.size()) {
+ LOG(_callbacks->getLogger(), USCXML_WARN) << "Transition exit set has wrong size: Cache corrupted";
+ } else {
+ _transitions[i]->exitSet = exitSet;
+ goto EXIT_SET_ESTABLISHED;
+ }
+ }
#endif
- std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml);
+ {
+ std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml);
for (j = 0; j < _states.size(); j++) {
if (!exitList.empty() && _states[j]->element == exitList.front()) {
@@ -420,54 +474,67 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) {
}
}
assert(exitList.size() == 0);
-
-#ifdef WITH_CACHE_FILES
- if (withCache)
- cachedTrans->compound["exitset"] = Data(toBase64(_transitions[i]->exitSet));
}
+#ifdef WITH_CACHE_FILES
+ if (withCache)
+ cachedTrans->compound["exitset"] = Data(toBase64(_transitions[i]->exitSet));
+EXIT_SET_ESTABLISHED:
#endif
// establish the transitions' conflict set
#ifdef WITH_CACHE_FILES
if (withCache && cachedTrans->compound.find("conflicts") != cachedTrans->compound.end()) {
- _transitions[i]->conflicts = fromBase64(cachedTrans->compound["conflicts"]);
- } else {
-#endif
- for (j = i; j < _transitions.size(); j++) {
- if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) {
- _transitions[i]->conflicts[j] = true;
- } else {
- _transitions[i]->conflicts[j] = false;
- }
- // std::cout << ".";
+ boost::dynamic_bitset<BITSET_BLOCKTYPE> conflicts = fromBase64(cachedTrans->compound["conflicts"]);
+ if (conflicts.size() != _transitions.size()) {
+ LOG(_callbacks->getLogger(), USCXML_WARN) << "Transition conflicts has wrong size: Cache corrupted";
+ } else {
+ _transitions[i]->conflicts = conflicts;
+ goto CONFLICTS_ESTABLISHED;
}
-
- // conflicts matrix is symmetric
- for (j = 0; j < i; j++) {
- _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i];
+ }
+#endif
+ for (j = i; j < _transitions.size(); j++) {
+ if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) {
+ _transitions[i]->conflicts[j] = true;
+ } else {
+ _transitions[i]->conflicts[j] = false;
}
-#ifdef WITH_CACHE_FILES
- if (withCache)
- cachedTrans->compound["conflicts"] = Data(toBase64(_transitions[i]->conflicts));
+ // std::cout << ".";
+ }
+ // conflicts matrix is symmetric
+ for (j = 0; j < i; j++) {
+ _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i];
}
+#ifdef WITH_CACHE_FILES
+ if (withCache)
+ cachedTrans->compound["conflicts"] = Data(toBase64(_transitions[i]->conflicts));
+CONFLICTS_ESTABLISHED:
#endif
// establish the transitions' target set
#ifdef WITH_CACHE_FILES
if (withCache && cachedTrans->compound.find("target") != cachedTrans->compound.end()) {
- _transitions[i]->target = fromBase64(cachedTrans->compound["target"]);
- } else {
+ boost::dynamic_bitset<BITSET_BLOCKTYPE> target = fromBase64(cachedTrans->compound["target"]);
+ if (target.size() != _states.size()) {
+ LOG(_callbacks->getLogger(), USCXML_WARN) << "Transition target set has wrong size: Cache corrupted";
+ } else {
+ _transitions[i]->target = target;
+ goto TARGET_SET_ESTABLISHED;
+ }
+ }
#endif
- std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target"));
+ {
+ std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target"));
for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) {
if (_stateIds.find(*tIter) != _stateIds.end()) {
_transitions[i]->target[_stateIds[*tIter]] = true;
}
}
-#ifdef WITH_CACHE_FILES
- if (withCache)
- cachedTrans->compound["target"] = Data(toBase64(_transitions[i]->target));
}
+#ifdef WITH_CACHE_FILES
+ if (withCache)
+ cachedTrans->compound["target"] = Data(toBase64(_transitions[i]->target));
+TARGET_SET_ESTABLISHED:
#endif
// the transition's source
State* uscxmlState = (State*)(_transitions[i]->element->getParentNode()->getUserData(X("uscxmlState")));
@@ -656,6 +723,7 @@ InterpreterState FastMicroStep::step(size_t blockMs) {
USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), onStableConfiguration);
_microstepConfigurations.clear();
_flags |= USCXML_CTX_STABLE;
+ return USCXML_MACROSTEPPED;
}
if ((_event = _callbacks->dequeueExternal(blockMs))) {
@@ -726,9 +794,9 @@ SELECT_TRANSITIONS:
_flags |= USCXML_CTX_SPONTANEOUS;
_flags &= ~USCXML_CTX_TRANSITION_FOUND;
} else {
- // spontaneuous transitions are exhausted
+ // spontaneuous transitions are exhausted and we will attempt to dequeue an internal event next round
_flags &= ~USCXML_CTX_SPONTANEOUS;
- return USCXML_MACROSTEPPED;
+ return USCXML_MICROSTEPPED;
}
USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), beforeMicroStep);
diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h
index db3e724..97b7eeb 100644
--- a/src/uscxml/interpreter/FastMicroStep.h
+++ b/src/uscxml/interpreter/FastMicroStep.h
@@ -57,6 +57,9 @@ public:
virtual std::list<XERCESC_NS::DOMElement*> getConfiguration();
void markAsCancelled();
+ virtual void deserialize(const Data& encodedState);
+ virtual Data serialize();
+
protected:
class Transition {
public:
diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp
index f9da040..60ce29d 100644
--- a/src/uscxml/interpreter/InterpreterImpl.cpp
+++ b/src/uscxml/interpreter/InterpreterImpl.cpp
@@ -111,7 +111,7 @@ InterpreterImpl::~InterpreterImpl() {
// ::xercesc_3_1::XMLPlatformUtils::Terminate();
#ifdef WITH_CACHE_FILES
- if (!envVarIsTrue("USCXML_NOCACHE_FILES")) {
+ if (!envVarIsTrue("USCXML_NOCACHE_FILES") && _document != NULL) {
// save our cache
std::string sharedTemp = URL::getTempDir(true);
std::ofstream dataFS(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache");
@@ -123,12 +123,139 @@ InterpreterImpl::~InterpreterImpl() {
#endif
}
+InterpreterState InterpreterImpl::step(size_t blockMs) {
+ std::lock_guard<std::recursive_mutex> lock(_serializationMutex);
+ if (!_isInitialized) {
+ init();
+ _state = USCXML_INITIALIZED;
+ } else {
+ _state = _microStepper.step(blockMs);
+ }
+ return _state;
+}
+
+void InterpreterImpl::reset() {
+ if (_microStepper)
+ _microStepper.reset();
+
+ _isInitialized = false;
+ _state = USCXML_INSTANTIATED;
+ // _dataModel.reset();
+ if (_delayQueue)
+ _delayQueue.reset();
+ // _contentExecutor.reset();
+}
+
void InterpreterImpl::cancel() {
+
_microStepper.markAsCancelled();
Event unblock;
enqueueExternal(unblock);
}
+void InterpreterImpl::deserialize(const std::string& encodedState) {
+
+ init();
+// std::cout << encodedState << std::endl;
+ Data state = Data::fromJSON(encodedState);
+ if (!state.hasKey("microstepper")) {
+ ERROR_PLATFORM_THROW("No microstepper information in serialized state");
+ }
+
+ if (!state.hasKey("url")) {
+ ERROR_PLATFORM_THROW("No url information in serialized state");
+ }
+
+ if (!state.hasKey("md5")) {
+ ERROR_PLATFORM_THROW("No MD5 hash in serialized state");
+ }
+
+ if (state.hasKey("externalQueue")) {
+ _externalQueue.deserialize(state["externalQueue"]);
+ }
+
+ if (state.hasKey("delayQueue")) {
+ _delayQueue.deserialize(state["delayQueue"]);
+ }
+
+ if (_md5.size() == 0) {
+ // get md5 of current document
+ std::stringstream ss;
+ ss << *_document;
+ _md5 = md5(ss.str());
+ }
+
+ if (state["md5"].atom != _md5) {
+ ERROR_PLATFORM_THROW("MD5 hash mismatch in serialized state");
+ }
+
+ std::list<XERCESC_NS::DOMElement*> datas = DOMUtils::inDocumentOrder({ XML_PREFIX(_scxml).str() + "data" }, _scxml);
+ for (auto data : datas) {
+ if (HAS_ATTR(data, "id") && state["datamodel"].hasKey(ATTR(data, "id")))
+ _dataModel.init(ATTR(data, "id"), state["datamodel"][ATTR(data, "id")]);
+ }
+
+ _microStepper.deserialize(state["microstepper"]);
+
+ std::list<XERCESC_NS::DOMElement*> invokes = DOMUtils::inDocumentOrder({ XML_PREFIX(_scxml).str() + "invoke" }, _scxml);
+ for (auto invokeElem : invokes) {
+ // BasicContentExecutor sets invokeid userdata upon invocation
+ char* invokeId = (char*)invokeElem->getUserData(X("invokeid"));
+ if (invokeId != NULL && _invokers.find(invokeId) != _invokers.end()) {
+ std::string path = DOMUtils::xPathForNode(invokeElem);
+ if (state.hasKey("invoker") && state["invoker"].hasKey(path)) {
+ _invokers[invokeId].deserialize(state["invoker"][path]);
+ }
+ }
+ }
+
+}
+
+std::string InterpreterImpl::serialize() {
+ std::lock_guard<std::recursive_mutex> lock(_serializationMutex);
+
+ Data serialized;
+ if (_state != USCXML_IDLE && _state != USCXML_MACROSTEPPED && _state != USCXML_FINISHED) {
+ ERROR_PLATFORM_THROW("Cannot serialize an unstable interpreter");
+ }
+
+ if (_md5.size() == 0) {
+ // get md5 of current document
+ std::stringstream ss;
+ ss << *_document;
+ _md5 = md5(ss.str());
+ }
+
+ serialized["md5"] = Data(_md5);
+ serialized["url"] = Data(std::string(_baseURL));
+ serialized["microstepper"] = _microStepper.serialize();
+
+ // SCXML Rec: "the values of all attributes of type "id" must be unique within the session"
+ std::list<XERCESC_NS::DOMElement*> datas = DOMUtils::inDocumentOrder({ XML_PREFIX(_scxml).str() + "data" }, _scxml);
+ for (auto data : datas) {
+ if (HAS_ATTR(data, "id")) {
+ serialized["datamodel"][ATTR(data, "id")] = _dataModel.evalAsData(ATTR(data, "id"));
+ }
+ }
+
+ // save all invokers' state
+ std::list<XERCESC_NS::DOMElement*> invokes = DOMUtils::inDocumentOrder({ XML_PREFIX(_scxml).str() + "invoke" }, _scxml);
+ for (auto invokeElem : invokes) {
+ // BasicContentExecutor sets invokeid userdata upon invocation
+ char* invokeId = (char*)invokeElem->getUserData(X("invokeid"));
+ if (invokeId != NULL && _invokers.find(invokeId) != _invokers.end()) {
+ std::string path = DOMUtils::xPathForNode(invokeElem);
+ serialized["invoker"][path] = _invokers[invokeId].serialize();
+ }
+ }
+
+// serialized["internalQueue"] = _internalQueue.serialize();
+ serialized["externalQueue"] = _externalQueue.serialize();
+ serialized["delayQueue"] = _delayQueue.serialize();
+
+ return serialized.asJSON();
+}
+
void InterpreterImpl::setupDOM() {
@@ -209,7 +336,7 @@ void InterpreterImpl::init() {
_cache.compound["InterpreterImpl"].compound["md5"] = Data(_md5);
}
#endif
-
+
// register io processors
std::map<std::string, IOProcessorImpl*> ioProcs = _factory->getIOProcessors();
for (auto ioProcIter = ioProcs.begin(); ioProcIter != ioProcs.end(); ioProcIter++) {
@@ -231,7 +358,6 @@ void InterpreterImpl::init() {
_ioProcs[*nameIter] = _ioProcs[ioProcIter->first];
}
}
-
}
if (!_microStepper) {
@@ -268,7 +394,13 @@ void InterpreterImpl::initData(XERCESC_NS::DOMElement* root) {
} else if (_invokeReq.namelist.find(id) != _invokeReq.namelist.end()) {
_dataModel.init(id, _invokeReq.namelist[id]);
} else {
- _dataModel.init(id, _execContent.elementAsData(root));
+ try {
+ _dataModel.init(id, _execContent.elementAsData(root));
+ } catch (ErrorEvent e) {
+ // test 453
+ _dataModel.init(id, _execContent.elementAsData(root, true));
+
+ }
}
} catch(ErrorEvent e) {
// test 277
diff --git a/src/uscxml/interpreter/InterpreterImpl.h b/src/uscxml/interpreter/InterpreterImpl.h
index 0efc70a..2b12624 100644
--- a/src/uscxml/interpreter/InterpreterImpl.h
+++ b/src/uscxml/interpreter/InterpreterImpl.h
@@ -64,30 +64,14 @@ public:
void cloneFrom(InterpreterImpl* other);
void cloneFrom(std::shared_ptr<InterpreterImpl> other);
- virtual InterpreterState step(size_t blockMs) {
- if (!_isInitialized) {
- init();
- _state = USCXML_INITIALIZED;
- } else {
- _state = _microStepper.step(blockMs);
- }
- return _state;
- }
-
- virtual void reset() {///< Reset state machine
- if (_microStepper)
- _microStepper.reset();
-
- _isInitialized = false;
- _state = USCXML_INSTANTIATED;
-// _dataModel.reset();
- if (_delayQueue)
- _delayQueue.reset();
-// _contentExecutor.reset();
- }
+ virtual InterpreterState step(size_t blockMs);
+ virtual void reset();///< Reset state machine
virtual void cancel(); ///< Cancel and finalize state machine
+ virtual void deserialize(const std::string& encodedState);
+ virtual std::string serialize();
+
InterpreterState getState() {
return _state;
}
@@ -240,6 +224,18 @@ public:
_delayQueue = al.delayedQueue;
}
+ ActionLanguage getActionLanguage() {
+ ActionLanguage al;
+ al.logger = _logger;
+ al.execContent = _execContent;
+ al.microStepper = _microStepper;
+ al.dataModel = _dataModel;
+ al.internalQueue = _internalQueue;
+ al.externalQueue = _externalQueue;
+ al.delayedQueue = _delayQueue;
+ return al;
+ }
+
void setFactory(Factory* factory) {
_factory = factory;
}
@@ -274,6 +270,7 @@ protected:
static std::map<std::string, std::weak_ptr<InterpreterImpl> > _instances;
static std::recursive_mutex _instanceMutex;
std::recursive_mutex _delayMutex;
+ std::recursive_mutex _serializationMutex;
friend class Interpreter;
friend class InterpreterIssue;
diff --git a/src/uscxml/interpreter/InterpreterMonitor.h b/src/uscxml/interpreter/InterpreterMonitor.h
index a3c527d..7207558 100644
--- a/src/uscxml/interpreter/InterpreterMonitor.h
+++ b/src/uscxml/interpreter/InterpreterMonitor.h
@@ -102,7 +102,7 @@ protected:
class USCXML_API StateTransitionMonitor : public uscxml::InterpreterMonitor {
public:
- StateTransitionMonitor() {}
+ StateTransitionMonitor(std::string prefix = "") : _logPrefix(prefix) {}
virtual ~StateTransitionMonitor() {}
virtual void beforeTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition);
@@ -115,6 +115,7 @@ public:
protected:
static std::recursive_mutex _mutex;
+ std::string _logPrefix;
};
}
diff --git a/src/uscxml/interpreter/MicroStep.cpp b/src/uscxml/interpreter/MicroStep.cpp
index 896c92a..e5de1dc 100644
--- a/src/uscxml/interpreter/MicroStep.cpp
+++ b/src/uscxml/interpreter/MicroStep.cpp
@@ -44,6 +44,14 @@ void MicroStep::markAsCancelled() {
_impl->markAsCancelled();
}
+void MicroStep::deserialize(const Data& encodedState) {
+ _impl->deserialize(encodedState);
+}
+
+Data MicroStep::serialize() {
+ return _impl->serialize();
+}
+
std::shared_ptr<MicroStepImpl> MicroStep::getImpl() const {
return _impl;
}
diff --git a/src/uscxml/interpreter/MicroStep.h b/src/uscxml/interpreter/MicroStep.h
index 56a7ee8..bda8fd7 100644
--- a/src/uscxml/interpreter/MicroStep.h
+++ b/src/uscxml/interpreter/MicroStep.h
@@ -26,6 +26,7 @@
#include <string>
#include "uscxml/Common.h"
+#include "uscxml/messages/Data.h"
#include "uscxml/interpreter/InterpreterState.h"
@@ -55,6 +56,12 @@ public:
virtual void init(XERCESC_NS::DOMElement* scxml);
virtual void markAsCancelled();
+ /// @copydoc MicroStepImpl::deserialize
+ virtual void deserialize(const Data& encodedState);
+
+ /// @copydoc MicroStepImpl::serialize
+ virtual Data serialize();
+
std::shared_ptr<MicroStepImpl> getImpl() const;
protected:
std::shared_ptr<MicroStepImpl> _impl;
diff --git a/src/uscxml/interpreter/MicroStepImpl.h b/src/uscxml/interpreter/MicroStepImpl.h
index e3f8299..7ff9469 100644
--- a/src/uscxml/interpreter/MicroStepImpl.h
+++ b/src/uscxml/interpreter/MicroStepImpl.h
@@ -61,8 +61,9 @@ public:
virtual Interpreter getInterpreter() = 0;
virtual Logger getLogger() = 0;
- /** Saved State */
+ /** Cache Data */
virtual Data& getCache() = 0;
+
};
/**
@@ -87,6 +88,9 @@ public:
virtual void init(XERCESC_NS::DOMElement* scxml) = 0;
virtual void markAsCancelled() = 0;
+ virtual void deserialize(const Data& encodedState) = 0;
+ virtual Data serialize() = 0;
+
protected:
MicroStepCallbacks* _callbacks;
diff --git a/src/uscxml/messages/Data.cpp b/src/uscxml/messages/Data.cpp
index de9644b..277b7d8 100644
--- a/src/uscxml/messages/Data.cpp
+++ b/src/uscxml/messages/Data.cpp
@@ -101,8 +101,7 @@ Data Data::fromJSON(const std::string& jsonString) {
}
t = (jsmntok_t*)malloc((nrTokens + 1) * sizeof(jsmntok_t));
if (t == NULL) {
- throw ErrorEvent("Cannot parse JSON, ran out of memory!");
- return data;
+ ERROR_PLATFORM_THROW("Cannot parse JSON, ran out of memory!");
}
memset(t, 0, (nrTokens + 1) * sizeof(jsmntok_t));
@@ -111,15 +110,18 @@ Data Data::fromJSON(const std::string& jsonString) {
if (rv != 0) {
switch (rv) {
- case JSMN_ERROR_NOMEM:
- throw ErrorEvent("Cannot parse JSON, not enough tokens were provided!");
+ case JSMN_ERROR_NOMEM: {
+ ERROR_PLATFORM_THROW("Cannot parse JSON, not enough tokens were provided!");
break;
- case JSMN_ERROR_INVAL:
- throw ErrorEvent("Cannot parse JSON, invalid character inside JSON string!");
+ }
+ case JSMN_ERROR_INVAL: {
+ ERROR_PLATFORM_THROW("Cannot parse JSON, invalid character inside JSON string!");
break;
- case JSMN_ERROR_PART:
- throw ErrorEvent("Cannot parse JSON, the string is not a full JSON packet, more bytes expected!");
+ }
+ case JSMN_ERROR_PART: {
+ ERROR_PLATFORM_THROW("Cannot parse JSON, the string is not a full JSON packet, more bytes expected!");
break;
+ }
default:
break;
}
@@ -151,10 +153,11 @@ Data Data::fromJSON(const std::string& jsonString) {
dataStack.back()->type = Data::VERBATIM;
case JSMN_PRIMITIVE: {
std::string value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start);
- if (dataStack.back()->type == Data::VERBATIM) {
- boost::replace_all(value, "\\\"", "\"");
- boost::replace_all(value, "\\n", "\n");
- }
+// if (dataStack.back()->type == Data::VERBATIM) {
+// boost::replace_all(value, "\\\"", "\"");
+// boost::replace_all(value, "\\n", "\n");
+// }
+ value = jsonUnescape(value);
dataStack.back()->atom = value;
dataStack.pop_back();
currTok++;
@@ -182,7 +185,7 @@ Data Data::fromJSON(const std::string& jsonString) {
if (tokenStack.back().type == JSMN_OBJECT && (t[currTok].type == JSMN_PRIMITIVE || t[currTok].type == JSMN_STRING)) {
// grab key and push new data
- std::string value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start);
+ std::string value = jsonUnescape(trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start));
dataStack.push_back(&(dataStack.back()->compound[value]));
currTok++;
}
@@ -230,7 +233,7 @@ std::string Data::toJSON(const Data& data) {
os << std::endl << indent << "{";
compoundIter = data.compound.begin();
while(compoundIter != data.compound.end()) {
- os << seperator << std::endl << indent << " \"" << compoundIter->first << "\": " << keyPadding.substr(0, longestKey - compoundIter->first.size());
+ os << seperator << std::endl << indent << " \"" << jsonEscape(compoundIter->first) << "\": " << keyPadding.substr(0, longestKey - compoundIter->first.size());
_dataIndentation += 1;
os << compoundIter->second;
_dataIndentation -= 1;
@@ -254,38 +257,110 @@ std::string Data::toJSON(const Data& data) {
} else if (data.atom.size() > 0) {
// empty string is handled below
if (data.type == Data::VERBATIM) {
- os << "\"";
- for (size_t i = 0; i < data.atom.size(); i++) {
- // escape string
- if (false) {
- } else if (data.atom[i] == '"') {
- os << "\\\"";
- } else if (data.atom[i] == '\n') {
- os << "\\n";
- } else if (data.atom[i] == '\t') {
- os << "\\t";
- } else {
- os << data.atom[i];
- }
- }
- os << "\"";
+ os << "\"" << jsonEscape(data.atom) << "\"";
} else {
os << data.atom;
}
} else if (data.node) {
std::ostringstream xmlSerSS;
- xmlSerSS << data.node;
+ xmlSerSS << *data.node;
std::string xmlSer = xmlSerSS.str();
- boost::replace_all(xmlSer, "\"", "\\\"");
- boost::replace_all(xmlSer, "\n", "\\n");
- boost::replace_all(xmlSer, "\t", "\\t");
- os << "\"" << xmlSer << "\"";
+// boost::replace_all(xmlSer, "\"", "\\\"");
+// boost::replace_all(xmlSer, "\n", "\\n");
+// boost::replace_all(xmlSer, "\t", "\\t");
+ os << "\"" << jsonEscape(xmlSer) << "\"";
} else {
if (data.type == Data::VERBATIM) {
os << "\"\""; // empty string
+ } else {
+ os << "null"; // non object
}
}
return os.str();
}
+std::string Data::jsonUnescape(const std::string& expr) {
+
+ // http://stackoverflow.com/a/19636328/990120
+ bool escape = false;
+ std::string output;
+ output.reserve(expr.length());
+
+ for (std::string::size_type i = 0; i < expr.length(); ++i) {
+ if (escape) {
+ switch(expr[i]) {
+ case '"':
+ output += '\"';
+ break;
+ case '/':
+ output += '/';
+ break;
+ case 'b':
+ output += '\b';
+ break;
+ case 'f':
+ output += '\f';
+ break;
+ case 'n':
+ output += '\n';
+ break;
+ case 'r':
+ output += '\r';
+ break;
+ case 't':
+ output += '\t';
+ break;
+ case '\\':
+ output += '\\';
+ break;
+ default:
+ output += expr[i];
+ break;
+ }
+ escape = false;
+ } else {
+ switch(expr[i]) {
+ case '\\':
+ escape = true;
+ break;
+ default:
+ output += expr[i];
+ break;
+ }
+ }
+ }
+ return output;
+
+}
+
+std::string Data::jsonEscape(const std::string& expr) {
+ std::stringstream os;
+ for (size_t i = 0; i < expr.size(); i++) {
+ // escape string
+ if (false) {
+ } else if (expr[i] == '\t') {
+ os << "\\t";
+ } else if (expr[i] == '\v') {
+ os << "\\v";
+ } else if (expr[i] == '\b') {
+ os << "\\b";
+ } else if (expr[i] == '\f') {
+ os << "\\f";
+ } else if (expr[i] == '\n') {
+ os << "\\n";
+ } else if (expr[i] == '\r') {
+ os << "\\r";
+ } else if (expr[i] == '\'') {
+ os << "\\'";
+ } else if (expr[i] == '\"') {
+ os << "\\\"";
+ } else if (expr[i] == '\\') {
+ os << "\\\\";
+ } else {
+ os << expr[i];
+ }
+ }
+
+ return os.str();
+}
}
diff --git a/src/uscxml/messages/Data.h b/src/uscxml/messages/Data.h
index 17d37cb..19134dd 100644
--- a/src/uscxml/messages/Data.h
+++ b/src/uscxml/messages/Data.h
@@ -71,14 +71,15 @@ public:
explicit Data(T value, Type type, typename std::enable_if<! std::is_base_of<Data, T>::value>::type* = nullptr)
: node(NULL), atom(toStr(value)), type(type) {}
- ~Data() {}
+ ~Data() {
+ }
void clear() {
type = VERBATIM;
compound.clear();
array.clear();
atom.clear();
- adoptedDoc.reset();
+// adoptedDoc.reset();
binary = Blob();
node = NULL;
}
@@ -234,7 +235,7 @@ protected:
#endif
XERCESC_NS::DOMNode* node;
- std::shared_ptr<XERCESC_NS::DOMDocument> adoptedDoc;
+// std::shared_ptr<XERCESC_NS::DOMDocument> adoptedDoc;
std::map<std::string, Data> compound;
std::list<Data> array;
std::string atom;
@@ -242,7 +243,10 @@ protected:
Type type;
protected:
+ static std::string jsonEscape(const std::string& expr);
+ static std::string jsonUnescape(const std::string& expr);
friend USCXML_API std::ostream& operator<< (std::ostream& os, const Data& data);
+
};
USCXML_API std::ostream& operator<< (std::ostream& os, const Data& data);
diff --git a/src/uscxml/messages/Event.cpp b/src/uscxml/messages/Event.cpp
index c4aa642..f372798 100644
--- a/src/uscxml/messages/Event.cpp
+++ b/src/uscxml/messages/Event.cpp
@@ -22,6 +22,62 @@
namespace uscxml {
+Event Event::fromData(const Data& data) {
+ Event e;
+ if (data.hasKey("data"))
+ e.data = Data(data["data"]);
+ if (data.hasKey("raw"))
+ e.raw = data["raw"].atom;
+ if (data.hasKey("name"))
+ e.name = data["name"].atom;
+ if (data.hasKey("eventType"))
+ e.eventType = (Type)strTo<size_t>(data["eventType"].atom);
+ if (data.hasKey("origin"))
+ e.origin = data["origin"].atom;
+ if (data.hasKey("origintype"))
+ e.origintype = data["origintype"].atom;
+ if (data.hasKey("sendid"))
+ e.sendid = data["sendid"].atom;
+ if (data.hasKey("hideSendId"))
+ e.hideSendId = strTo<bool>(data["hideSendId"].atom);
+ if (data.hasKey("invokeid"))
+ e.invokeid = data["invokeid"].atom;
+ if (data.hasKey("uuid"))
+ e.uuid = data["uuid"].atom;
+ if (data.hasKey("namelist"))
+ e.namelist = data["namelist"].compound;
+
+ if (data.hasKey("params")) {
+ for (auto param : data["params"].array) {
+ e.params.insert(std::make_pair(param.compound.begin()->first, param.compound.begin()->second));
+ }
+ }
+ return e;
+}
+
+Event::operator Data() {
+ Data data;
+ data["data"] = data;
+ data["raw"] = Data(raw, Data::VERBATIM);
+ data["name"] = Data(name, Data::VERBATIM);
+ data["eventType"] = Data(eventType, Data::VERBATIM);
+ data["origin"] = Data(origin, Data::VERBATIM);
+ data["origintype"] = Data(origintype, Data::VERBATIM);
+ data["sendid"] = Data(sendid, Data::VERBATIM);
+ data["hideSendId"] = Data(hideSendId, Data::VERBATIM);
+ data["invokeid"] = Data(invokeid, Data::VERBATIM);
+ data["uuid"] = Data(uuid, Data::VERBATIM);
+ data["namelist"].compound = namelist;
+
+ for (auto param : params) {
+ Data entry;
+ entry.compound[param.first] = param.second;
+ data["params"].array.push_back(entry);
+ }
+
+ return data;
+}
+
std::ostream& operator<< (std::ostream& os, const Event& event) {
std::string indent;
for (size_t i = 0; i < _dataIndentation; i++) {
@@ -67,4 +123,4 @@ std::ostream& operator<< (std::ostream& os, const Event& event) {
return os;
}
-} \ No newline at end of file
+}
diff --git a/src/uscxml/messages/Event.h b/src/uscxml/messages/Event.h
index 59b2690..b774f8a 100644
--- a/src/uscxml/messages/Event.h
+++ b/src/uscxml/messages/Event.h
@@ -24,20 +24,26 @@
#include "uscxml/util/UUID.h"
#define ERROR_PLATFORM_THROW(msg) \
- ErrorEvent e; \
+ uscxml::ErrorEvent e; \
e.name = "error.platform"; \
- e.data.compound["cause"] = Data(msg, Data::VERBATIM); \
+ e.data.compound["cause"] = uscxml::Data(msg, uscxml::Data::VERBATIM); \
+ e.data.compound["file"] = uscxml::Data(uscxml::toStr(__FILE__), uscxml::Data::VERBATIM); \
+ e.data.compound["line"] = uscxml::Data(uscxml::toStr(__LINE__), uscxml::Data::INTERPRETED); \
throw e; \
#define ERROR_EXECUTION(identifier, cause) \
uscxml::ErrorEvent identifier; \
identifier.data.compound["cause"] = uscxml::Data(cause, uscxml::Data::VERBATIM); \
+ identifier.data.compound["file"] = uscxml::Data(uscxml::toStr(__FILE__), uscxml::Data::VERBATIM); \
+ identifier.data.compound["line"] = uscxml::Data(uscxml::toStr(__LINE__), uscxml::Data::INTERPRETED); \
identifier.name = "error.execution"; \
identifier.eventType = uscxml::Event::PLATFORM;
#define ERROR_EXECUTION2(identifier, cause, node) \
uscxml::ErrorEvent identifier; \
identifier.data.compound["cause"] = uscxml::Data(cause, uscxml::Data::VERBATIM); \
+ identifier.data.compound["file"] = uscxml::Data(uscxml::toStr(__FILE__), uscxml::Data::VERBATIM); \
+ identifier.data.compound["line"] = uscxml::Data(uscxml::toStr(__LINE__), uscxml::Data::INTERPRETED); \
identifier.name = "error.execution"; \
identifier.data.compound["xpath"] = uscxml::Data(DOMUtils::xPathForNode(node), uscxml::Data::VERBATIM); \
identifier.eventType = uscxml::Event::PLATFORM;
@@ -45,12 +51,16 @@
#define ERROR_COMMUNICATION(identifier, cause) \
uscxml::ErrorEvent identifier; \
identifier.data.compound["cause"] = uscxml::Data(cause, uscxml::Data::VERBATIM); \
+ identifier.data.compound["file"] = uscxml::Data(uscxml::toStr(__FILE__), uscxml::Data::VERBATIM); \
+ identifier.data.compound["line"] = uscxml::Data(uscxml::toStr(__LINE__), uscxml::Data::INTERPRETED); \
identifier.name = "error.communication"; \
identifier.eventType = uscxml::Event::PLATFORM;
#define ERROR_COMMUNICATION2(identifier, cause, node) \
uscxml::ErrorEvent identifier; \
identifier.data.compound["cause"] = uscxml::Data(cause, uscxml::Data::VERBATIM); \
+ identifier.data.compound["file"] = uscxml::Data(uscxml::toStr(__FILE__), uscxml::Data::VERBATIM); \
+ identifier.data.compound["line"] = uscxml::Data(uscxml::toStr(__LINE__), uscxml::Data::INTERPRETED); \
identifier.name = "error.communication"; \
identifier.data.compound["xpath"] = uscxml::Data(DOMUtils::xPathForNode(node), uscxml::Data::VERBATIM); \
identifier.eventType = uscxml::Event::PLATFORM;
@@ -91,6 +101,8 @@ public:
Event() : eventType(INTERNAL), hideSendId(false), uuid(UUID::getUUID()) {}
explicit Event(const std::string& name, Type type = INTERNAL) : name(name), eventType(type), hideSendId(false) {}
+ static Event fromData(const Data& data);
+
bool operator< (const Event& other) const {
return this < &other;
}
@@ -109,6 +121,8 @@ public:
return name.size() > 0;
}
+ operator Data();
+
operator std::string() {
std::stringstream ss;
ss << *this;
diff --git a/src/uscxml/plugins/Invoker.cpp b/src/uscxml/plugins/Invoker.cpp
index a021ff7..82c15f3 100644
--- a/src/uscxml/plugins/Invoker.cpp
+++ b/src/uscxml/plugins/Invoker.cpp
@@ -40,4 +40,13 @@ XERCESC_NS::DOMElement* Invoker::getFinalize() {
return _impl->getFinalize();
}
+void Invoker::deserialize(const Data& encodedState) {
+ return _impl->deserialize(encodedState);
+}
+
+Data Invoker::serialize() {
+ return _impl->serialize();
+}
+
+
}
diff --git a/src/uscxml/plugins/Invoker.h b/src/uscxml/plugins/Invoker.h
index 2191e7b..bb01ddd 100644
--- a/src/uscxml/plugins/Invoker.h
+++ b/src/uscxml/plugins/Invoker.h
@@ -56,6 +56,11 @@ public:
/// @copydoc InvokerImpl::getFinalize
virtual XERCESC_NS::DOMElement* getFinalize();
+ /// @copydoc InvokerImpl::deserialize
+ virtual void deserialize(const Data& encodedState);
+
+ /// @copydoc InvokerImpl::serialize
+ virtual Data serialize();
protected:
std::shared_ptr<InvokerImpl> _impl;
};
diff --git a/src/uscxml/plugins/InvokerImpl.h b/src/uscxml/plugins/InvokerImpl.h
index b000ce2..e0446e1 100644
--- a/src/uscxml/plugins/InvokerImpl.h
+++ b/src/uscxml/plugins/InvokerImpl.h
@@ -90,6 +90,19 @@ public:
_invokeId = invokeId;
}
+ /**
+ * Load a state from a Data object
+ * @param encodedState The state we returned somewhen else via serialize.
+ */
+ virtual void deserialize(const Data& encodedState) {}
+
+ /**
+ * Save our state into a Data object
+ */
+ virtual Data serialize() {
+ return Data();
+ }
+
protected:
/**
* Return an event to the SCXML Interpreter instance.
diff --git a/src/uscxml/plugins/datamodel/CMakeLists.txt b/src/uscxml/plugins/datamodel/CMakeLists.txt
index e68fc8f..a208856 100644
--- a/src/uscxml/plugins/datamodel/CMakeLists.txt
+++ b/src/uscxml/plugins/datamodel/CMakeLists.txt
@@ -66,7 +66,7 @@ if (WITH_DM_PROMELA)
promela/*.c
promela/*.h
)
- list (APPEND USCXML_FILES ${PROMELA_DATAMODEL})
+ list (APPEND USCXML_FILES ${PROMELA_DATAMODEL})
endif()
if (NOT SWIG_FOUND)
diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
index aae0111..22e8ccc 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
@@ -395,8 +395,14 @@ void JSCDataModel::setEvent(const Event& event) {
}
Data JSCDataModel::evalAsData(const std::string& content) {
- JSValueRef result = evalAsValue(content);
- return getValueAsData(result);
+ try {
+ JSValueRef result = evalAsValue(content);
+ return getValueAsData(result);
+ } catch (ErrorEvent e) {
+ // test453 vs test554
+ throw e;
+// return Data(content, Data::INTERPRETED);
+ }
}
Data JSCDataModel::getAsData(const std::string& content) {
@@ -593,7 +599,7 @@ void JSCDataModel::setForeach(const std::string& item,
const std::string& index,
uint32_t iteration) {
if (!isDeclared(item)) {
- assign(item, Data());
+ assign(item, Data("null", Data::INTERPRETED));
}
// assign array element to item
std::stringstream ss;
@@ -710,7 +716,11 @@ void JSCDataModel::assign(const std::string& location, const Data& data) {
void JSCDataModel::init(const std::string& location, const Data& data) {
try {
- assign(location, data);
+ if (data.empty()) {
+ assign(location, Data("null", Data::INTERPRETED));
+ } else {
+ assign(location, data);
+ }
} catch (ErrorEvent e) {
// test 277
evalAsValue(location + " = undefined", true);
diff --git a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp
index 868fee7..0f7cc24 100644
--- a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp
+++ b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp
@@ -75,6 +75,29 @@ void USCXMLInvoker::stop() {
}
}
+void USCXMLInvoker::deserialize(const Data& encodedState) {
+ _invokedInterpreter.deserialize(encodedState["intepreter"]);
+}
+
+Data USCXMLInvoker::serialize() {
+ Data encodedState;
+
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+
+ InterpreterState state = USCXML_UNDEF;
+ while((state = _invokedInterpreter.getState())) {
+ if (state != USCXML_IDLE && state != USCXML_MACROSTEPPED && state != USCXML_FINISHED) {
+ _cond.wait(_mutex);
+ } else {
+ break;
+ }
+ }
+
+ encodedState["intepreter"] = Data(_invokedInterpreter.serialize());
+
+ return encodedState;
+}
+
void USCXMLInvoker::uninvoke() {
_isActive = false;
stop();
@@ -100,13 +123,9 @@ void USCXMLInvoker::run(void* instance) {
InterpreterState state = USCXML_UNDEF;
while(state != USCXML_FINISHED) {
- state = INSTANCE->_invokedInterpreter.step();
-
-// if (!INSTANCE->_isStarted) {
-// // we have been cancelled
-// INSTANCE->_isActive = false;
-// return;
-// }
+ std::lock_guard<std::recursive_mutex> lock(INSTANCE->_mutex);
+ state = INSTANCE->_invokedInterpreter.step(200);
+ INSTANCE->_cond.notify_all();
}
if (INSTANCE->_isActive) {
@@ -143,10 +162,11 @@ void USCXMLInvoker::invoke(const std::string& source, const Event& invokeEvent)
XERCESC_NS::DOMNode* newNode = document->importNode(invokeEvent.data.node, true);
document->appendChild(newNode);
-// std::cout << *document << std::endl;
-
// TODO: where do we get the namespace from?
_invokedInterpreter = Interpreter::fromDocument(document, _interpreter->getBaseURL(), false);
+ } else if (invokeEvent.data.atom.size() > 0) {
+ // test530 when deserializing
+ _invokedInterpreter = Interpreter::fromXML(invokeEvent.data.atom, _interpreter->getBaseURL());
} else {
_isActive = false;
@@ -191,7 +211,8 @@ void USCXMLInvoker::invoke(const std::string& source, const Event& invokeEvent)
start();
} else {
- /// test 530
+ // test 530
+ // TODO: Is this the correct thing/place to do?
Event e("done.invoke." + invokeEvent.invokeid, Event::PLATFORM);
eventToSCXML(e, USCXML_INVOKER_SCXML_TYPE, _invokeId);
_isActive = false;
diff --git a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h
index 9509de3..78e7057 100644
--- a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h
+++ b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h
@@ -67,6 +67,9 @@ public:
virtual void invoke(const std::string& source, const Event& invokeEvent);
virtual void uninvoke();
+ virtual void deserialize(const Data& encodedState);
+ virtual Data serialize();
+
protected:
void start();
@@ -78,6 +81,10 @@ protected:
std::thread* _thread;
EventQueue _parentQueue;
Interpreter _invokedInterpreter;
+
+ std::recursive_mutex _mutex;
+ std::condition_variable_any _cond;
+
};
#ifdef BUILD_AS_PLUGINS
diff --git a/src/uscxml/util/DOM.cpp b/src/uscxml/util/DOM.cpp
index bc628b7..f661ebb 100644
--- a/src/uscxml/util/DOM.cpp
+++ b/src/uscxml/util/DOM.cpp
@@ -252,6 +252,8 @@ void DOMUtils::inPostFixOrder(const std::set<std::string>& elements,
}
}
+//TODO: Unify recursive search in DOM
+
std::list<DOMElement*> DOMUtils::inDocumentOrder(const std::set<std::string>& elements,
const DOMElement* root,
const bool includeEmbeddedDoc) {