summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--config.h.in1
-rw-r--r--src/uscxml/Interpreter.cpp17
-rw-r--r--src/uscxml/Interpreter.h9
-rw-r--r--src/uscxml/concurrency/DelayedEventQueue.cpp10
-rw-r--r--src/uscxml/concurrency/DelayedEventQueue.h3
-rw-r--r--src/uscxml/concurrency/Timer.cpp116
-rw-r--r--src/uscxml/concurrency/Timer.h68
-rw-r--r--src/uscxml/plugins/DataModel.h9
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp31
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h1
-rw-r--r--src/uscxml/transform/ChartToC.cpp430
-rw-r--r--src/uscxml/transform/ChartToC.h3
-rw-r--r--src/uscxml/transform/ChartToMinimalSCXML.h1
-rw-r--r--test/CMakeLists.txt1
-rw-r--r--test/ctest/CTestCustom.ctest.in37
-rw-r--r--test/src/test-c-machine.cpp375
-rw-r--r--test/src/test-c-machine.machine.c400
-rw-r--r--test/src/test-misc.cpp23
-rw-r--r--test/src/test-w3c.cpp51
-rw-r--r--test/w3c/run_generated_c_test.cmake4
21 files changed, 1278 insertions, 313 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bee9942..9fe4698 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -372,6 +372,7 @@ else()
endif()
OPTION(ENABLE_GCOV "Compile with gcov support" OFF)
+OPTION(BUILD_PROFILING "Build with profiling information" OFF)
OPTION(BUILD_MINIMAL "Build only features mandated by specification" OFF)
OPTION(BUILD_DM_ECMA "Build with ECMAScript datamodel" ON)
OPTION(BUILD_DM_XPATH "Build with XPath datamodel" ON)
diff --git a/config.h.in b/config.h.in
index 9527faa..bae7de8 100644
--- a/config.h.in
+++ b/config.h.in
@@ -53,6 +53,7 @@
/** miscellaneous */
#cmakedefine PROJECT_SOURCE_DIR "@PROJECT_SOURCE_DIR@"
#cmakedefine DIST_PREPARE
+#cmakedefine BUILD_PROFILING
#cmakedefine SWI_BINARY "@SWI_BINARY@"
/** whether we want some feature */
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 169d9f6..736a740 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -57,6 +57,12 @@
# include "uscxml/interpreter/InterpreterRC.h"
#endif
+#ifdef BUILD_PROFILING
+#define TIME_BLOCK Measurement msm(&timer);
+#else
+#define TIME_BLOCK (0);
+#endif
+
#define VERBOSE 0
/// valid interpreter state transitions
@@ -772,7 +778,8 @@ NodeSet<std::string> InterpreterImpl::getDocumentInitialTransitions() {
}
InterpreterState InterpreterImpl::step(int waitForMS) {
- try {
+ TIME_BLOCK
+ try {
tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
if (_state == USCXML_FINISHED || _state == USCXML_DESTROYED) {
@@ -967,8 +974,8 @@ EXIT_INTERPRETER:
_mutex.unlock();
// remove datamodel
- if(!_userSuppliedDataModel)
- _dataModel = DataModel();
+// if(!_userSuppliedDataModel)
+// _dataModel = DataModel();
setInterpreterState(USCXML_FINISHED);
return _state;
@@ -1362,6 +1369,10 @@ void InterpreterImpl::reset() {
_isInitialized = false;
_stable = false;
+#ifdef BUILD_PROFILING
+ timer = Timer();
+#endif
+ _dataModel = DataModel();
setInterpreterState(USCXML_INSTANTIATED);
}
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index 945f6f2..e442585 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -21,6 +21,7 @@
#define RUNTIME_H_SQ1MBKGN
// this has to be the first include or MSVC will run amok
+#include "uscxml/config.h"
#include "uscxml/Common.h"
#include <iostream> // arabica xpath uses cerr without iostream
@@ -49,6 +50,10 @@
#include "uscxml/plugins/Invoker.h"
#include "uscxml/plugins/ExecutableContent.h"
+#ifdef BUILD_PROFILING
+#include "uscxml/concurrency/Timer.h"
+#endif
+
#define ERROR_PLATFORM_THROW(msg) \
Event e; \
e.name = "error.platform"; \
@@ -407,6 +412,10 @@ public:
virtual void handleDOMEvent(Arabica::DOM::Events::Event<std::string>& event);
+#ifdef BUILD_PROFILING
+ Timer timer;
+#endif
+
protected:
static void run(void*); // static method for thread to run
diff --git a/src/uscxml/concurrency/DelayedEventQueue.cpp b/src/uscxml/concurrency/DelayedEventQueue.cpp
index 642c4a0..ca43c8e 100644
--- a/src/uscxml/concurrency/DelayedEventQueue.cpp
+++ b/src/uscxml/concurrency/DelayedEventQueue.cpp
@@ -99,6 +99,15 @@ void DelayedEventQueue::addEvent(std::string eventId, void (*callback)(void*, co
event_add(event, &delay);
}
+void DelayedEventQueue::cancelAllEvents() {
+ tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
+ while(_callbackData.size() > 0) {
+ event_del(_callbackData[_callbackData.begin()->first].event);
+ event_free(_callbackData[_callbackData.begin()->first].event);
+ _callbackData.erase(_callbackData.begin());
+ }
+}
+
void DelayedEventQueue::cancelEvent(std::string eventId) {
tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
@@ -122,6 +131,7 @@ void DelayedEventQueue::stop() {
if (_thread) {
_thread->join();
delete _thread;
+ _thread = NULL;
}
}
diff --git a/src/uscxml/concurrency/DelayedEventQueue.h b/src/uscxml/concurrency/DelayedEventQueue.h
index 2248c47..d89b715 100644
--- a/src/uscxml/concurrency/DelayedEventQueue.h
+++ b/src/uscxml/concurrency/DelayedEventQueue.h
@@ -57,7 +57,8 @@ public:
void addEvent(std::string eventId, int fd, short opMask, void (*callback)(void*, const std::string eventId), void* userData, bool persist = true);
void addEvent(std::string eventId, void (*callback)(void*, const std::string eventId), uint32_t delayMs, void* userData, bool persist = false);
- void cancelEvent(std::string eventId);
+ void cancelEvent(std::string eventId);
+ void cancelAllEvents();
void start();
void stop();
diff --git a/src/uscxml/concurrency/Timer.cpp b/src/uscxml/concurrency/Timer.cpp
new file mode 100644
index 0000000..b05a907
--- /dev/null
+++ b/src/uscxml/concurrency/Timer.cpp
@@ -0,0 +1,116 @@
+// Copyright 2013 Alex Reece.
+//
+// A cross platform monotonic timer.
+
+// see https://github.com/awreece/monotonic_timer
+
+#include <unistd.h>
+#include "Timer.h"
+
+#define NANOS_PER_SECF 1000000000.0
+#define USECS_PER_SEC 1000000
+
+#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
+ // If we have it, use clock_gettime and CLOCK_MONOTONIC.
+
+ #include <time.h>
+
+ double uscxml::Timer::monotonic_seconds() {
+ struct timespec time;
+ // Note: Make sure to link with -lrt to define clock_gettime.
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ return ((double) time.tv_sec) + ((double) time.tv_nsec / (NANOS_PER_SECF));
+ }
+
+#elif defined(__APPLE__)
+ // If we don't have CLOCK_MONOTONIC, we might be on a Mac. There we instead
+ // use mach_absolute_time().
+
+ #include <mach/mach_time.h>
+
+ static mach_timebase_info_data_t info;
+ static void __attribute__((constructor)) init_info() {
+ mach_timebase_info(&info);
+ }
+
+ double uscxml::Timer::monotonic_seconds() {
+ uint64_t time = mach_absolute_time();
+ double dtime = (double) time;
+ dtime *= (double) info.numer;
+ dtime /= (double) info.denom;
+ return dtime / NANOS_PER_SECF;
+ }
+
+#elif defined(_MSC_VER)
+ // On Windows, use QueryPerformanceCounter and QueryPerformanceFrequency.
+
+ #include <windows.h>
+
+ static double PCFreq = 0.0;
+
+ // According to http://stackoverflow.com/q/1113409/447288, this will
+ // make this function a constructor.
+ // TODO(awreece) Actually attempt to compile on windows.
+ static void __cdecl init_pcfreq();
+ __declspec(allocate(".CRT$XCU")) void (__cdecl*init_pcfreq_)() = init_pcfreq;
+ static void __cdecl init_pcfreq() {
+ // Accoring to http://stackoverflow.com/a/1739265/447288, this will
+ // properly initialize the QueryPerformanceCounter.
+ LARGE_INTEGER li;
+ int has_qpc = QueryPerformanceFrequency(&li);
+ assert(has_qpc);
+
+ PCFreq = ((double) li.QuadPart) / 1000.0;
+ }
+
+ double uscxml::Timer::monotonic_seconds() {
+ LARGE_INTEGER li;
+ QueryPerformanceCounter(&li);
+ return ((double) li.QuadPart) / PCFreq;
+ }
+
+#else
+ // Fall back to rdtsc. The reason we don't use clock() is this scary message
+ // from the man page:
+ // "On several other implementations, the value returned by clock() also
+ // includes the times of any children whose status has been collected via
+ // wait(2) (or another wait-type call)."
+ //
+ // Also, clock() only has microsecond accuracy.
+ //
+ // This whitepaper offered excellent advice on how to use rdtscp for
+ // profiling: http://download.intel.com/embedded/software/IA/324264.pdf
+ //
+ // Unfortunately, we can't follow its advice exactly with our semantics,
+ // so we're just going to use rdtscp with cpuid.
+ //
+ // Note that rdtscp will only be available on new processors.
+
+ #include <stdint.h>
+
+ static inline uint64_t rdtsc() {
+ uint32_t hi, lo;
+ asm volatile("rdtscp\n"
+ "movl %%edx, %0\n"
+ "movl %%eax, %1\n"
+ "cpuid"
+ : "=r" (hi), "=r" (lo) : : "%rax", "%rbx", "%rcx", "%rdx");
+ return (((uint64_t)hi) << 32) | (uint64_t)lo;
+ }
+
+ static uint64_t rdtsc_per_sec = 0;
+ static void __attribute__((constructor)) init_rdtsc_per_sec() {
+ uint64_t before, after;
+
+ before = rdtsc();
+ usleep(USECS_PER_SEC);
+ after = rdtsc();
+
+ rdtsc_per_sec = after - before;
+ }
+
+ double uscxml::Timer::monotonic_seconds() {
+ return (double) rdtsc() / (double) rdtsc_per_sec;
+ }
+
+#endif
diff --git a/src/uscxml/concurrency/Timer.h b/src/uscxml/concurrency/Timer.h
new file mode 100644
index 0000000..60a20a3
--- /dev/null
+++ b/src/uscxml/concurrency/Timer.h
@@ -0,0 +1,68 @@
+// Copyright 2013 Alex Reece.
+//
+// A cross platform monotonic timer.
+
+// see https://github.com/awreece/monotonic_timer
+
+#ifndef MONOTONIC_TIMER_H_
+#define MONOTONIC_TIMER_H_
+
+// Returns seconds since some unspecified start time (guaranteed to be
+// monotonicly increasing).
+
+// Copyright 2015 Stefan Radomski.
+
+namespace uscxml {
+
+class USCXML_API Timer {
+public:
+
+ static double monotonic_seconds();
+
+ Timer() {
+ invocations = 0;
+ elapsed = 0;
+ }
+
+ void start() {
+ if (invocations == 0) {
+ started = monotonic_seconds();
+ }
+ invocations++;
+ }
+
+ void stop() {
+ if (invocations == 0)
+ return;
+
+ invocations--;
+ if (invocations == 0) {
+ elapsed += monotonic_seconds() - started;
+ }
+ }
+
+ ~Timer() {
+ }
+ double elapsed;
+
+protected:
+ size_t invocations;
+ double started;
+};
+
+class USCXML_API Measurement {
+public:
+ Measurement(Timer* timer) : timer(timer) {
+ timer->start();
+ }
+
+ ~Measurement() {
+ timer->stop();
+ }
+
+protected:
+ Timer* timer;
+};
+
+}
+#endif // MONOTONIC_TIMER_H_
diff --git a/src/uscxml/plugins/DataModel.h b/src/uscxml/plugins/DataModel.h
index 563186e..097fcfd 100644
--- a/src/uscxml/plugins/DataModel.h
+++ b/src/uscxml/plugins/DataModel.h
@@ -20,9 +20,14 @@
#ifndef DATAMODEL_H_F1F776F9
#define DATAMODEL_H_F1F776F9
+#include "uscxml/config.h"
#include "uscxml/Common.h"
#include "uscxml/plugins/EventHandler.h"
+#ifdef BUILD_PROFILING
+#include "uscxml/concurrency/Timer.h"
+#endif
+
#include <list>
#include <boost/shared_ptr.hpp>
#include <string>
@@ -102,6 +107,10 @@ public:
return "";
}
+#ifdef BUILD_PROFILING
+ Timer timer;
+#endif
+
protected:
InterpreterInfo* _interpreter;
};
diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
index 551b590..9a71ab0 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
@@ -19,6 +19,7 @@
#include "uscxml/Common.h"
#include "uscxml/config.h"
+
#include "JSCDataModel.h"
#include "JSCDOM.h"
#include "dom/JSCDocument.h"
@@ -43,6 +44,12 @@
#include "uscxml/DOMUtils.h"
#include <glog/logging.h>
+#ifdef BUILD_PROFILING
+#define TIME_BLOCK Measurement msm(&timer);
+#else
+#define TIME_BLOCK (0);
+#endif
+
#ifdef BUILD_AS_PLUGINS
#include <Pluma/Connector.hpp>
#endif
@@ -86,6 +93,7 @@ JSCDataModel::~JSCDataModel() {
}
void JSCDataModel::addExtension(DataModelExtension* ext) {
+ TIME_BLOCK
if (_extensions.find(ext) != _extensions.end())
return;
@@ -191,7 +199,8 @@ JSClassDefinition JSCDataModel::jsInvokersClassDef = { 0, 0, "invokers", 0, 0, 0
boost::shared_ptr<DataModelImpl> JSCDataModel::create(InterpreterInfo* interpreter) {
boost::shared_ptr<JSCDataModel> dm = boost::shared_ptr<JSCDataModel>(new JSCDataModel());
-
+ TIME_BLOCK
+
dm->_ctx = JSGlobalContextCreate(NULL);
dm->_interpreter = interpreter;
@@ -275,6 +284,7 @@ void JSCDataModel::popContext() {
}
void JSCDataModel::setEvent(const Event& event) {
+ TIME_BLOCK
JSCSCXMLEvent::JSCSCXMLEventPrivate* privData = new JSCSCXMLEvent::JSCSCXMLEventPrivate();
privData->nativeObj = new Event(event);
privData->dom = _dom;
@@ -357,12 +367,14 @@ void JSCDataModel::setEvent(const Event& event) {
}
Data JSCDataModel::getStringAsData(const std::string& content) {
+ TIME_BLOCK
JSValueRef result = evalAsValue(content);
Data data = getValueAsData(result);
return data;
}
JSValueRef JSCDataModel::getDataAsValue(const Data& data) {
+ TIME_BLOCK
JSValueRef exception = NULL;
if (data.node) {
@@ -428,6 +440,7 @@ JSValueRef JSCDataModel::getDataAsValue(const Data& data) {
}
Data JSCDataModel::getValueAsData(const JSValueRef value) {
+ TIME_BLOCK
Data data;
JSValueRef exception = NULL;
switch(JSValueGetType(_ctx, value)) {
@@ -516,10 +529,12 @@ Data JSCDataModel::getValueAsData(const JSValueRef value) {
}
bool JSCDataModel::validate(const std::string& location, const std::string& schema) {
+ TIME_BLOCK
return true;
}
uint32_t JSCDataModel::getLength(const std::string& expr) {
+ TIME_BLOCK
JSValueRef result;
result = evalAsValue("(" + expr + ").length");
@@ -540,6 +555,7 @@ void JSCDataModel::setForeach(const std::string& item,
const std::string& array,
const std::string& index,
uint32_t iteration) {
+ TIME_BLOCK
if (!isDeclared(item)) {
assign(item, Data());
}
@@ -556,11 +572,13 @@ void JSCDataModel::setForeach(const std::string& item,
}
bool JSCDataModel::isLocation(const std::string& expr) {
+ TIME_BLOCK
// location needs to be LHS and ++ is only valid for LHS
return isValidSyntax(expr + "++");
}
bool JSCDataModel::isValidSyntax(const std::string& expr) {
+ TIME_BLOCK
JSStringRef scriptJS = JSStringCreateWithUTF8CString(expr.c_str());
JSValueRef exception = NULL;
bool valid = JSCheckScriptSyntax(_ctx, scriptJS, NULL, 0, &exception);
@@ -573,6 +591,7 @@ bool JSCDataModel::isValidSyntax(const std::string& expr) {
}
bool JSCDataModel::isDeclared(const std::string& expr) {
+ TIME_BLOCK
JSStringRef scriptJS = JSStringCreateWithUTF8CString(expr.c_str());
JSValueRef exception = NULL;
JSValueRef result = JSEvaluateScript(_ctx, scriptJS, NULL, NULL, 0, &exception);
@@ -586,15 +605,18 @@ bool JSCDataModel::isDeclared(const std::string& expr) {
void JSCDataModel::eval(const Element<std::string>& scriptElem,
const std::string& expr) {
+ TIME_BLOCK
evalAsValue(expr);
}
bool JSCDataModel::evalAsBool(const Arabica::DOM::Element<std::string>& node, const std::string& expr) {
+ TIME_BLOCK
JSValueRef result = evalAsValue(expr);
return JSValueToBoolean(_ctx, result);
}
std::string JSCDataModel::evalAsString(const std::string& expr) {
+ TIME_BLOCK
JSValueRef result = evalAsValue(expr);
JSValueRef exception = NULL;
@@ -613,6 +635,7 @@ std::string JSCDataModel::evalAsString(const std::string& expr) {
}
JSValueRef JSCDataModel::evalAsValue(const std::string& expr, bool dontThrow) {
+ TIME_BLOCK
JSStringRef scriptJS = JSStringCreateWithUTF8CString(expr.c_str());
JSValueRef exception = NULL;
JSValueRef result = JSEvaluateScript(_ctx, scriptJS, NULL, NULL, 0, &exception);
@@ -627,6 +650,7 @@ JSValueRef JSCDataModel::evalAsValue(const std::string& expr, bool dontThrow) {
void JSCDataModel::assign(const Element<std::string>& assignElem,
const Node<std::string>& node,
const std::string& content) {
+ TIME_BLOCK
std::string key;
JSValueRef exception = NULL;
if (HAS_ATTR(assignElem, "id")) {
@@ -669,6 +693,7 @@ void JSCDataModel::assign(const Element<std::string>& assignElem,
}
JSValueRef JSCDataModel::getNodeAsValue(const Node<std::string>& node) {
+ TIME_BLOCK
switch (node.getNodeType()) {
case Node_base::ELEMENT_NODE: {
TO_JSC_DOMVALUE(Element);
@@ -689,6 +714,7 @@ JSValueRef JSCDataModel::getNodeAsValue(const Node<std::string>& node) {
}
void JSCDataModel::assign(const std::string& location, const Data& data) {
+ TIME_BLOCK
std::stringstream ssJSON;
ssJSON << data;
evalAsValue(location + " = " + ssJSON.str());
@@ -697,6 +723,7 @@ void JSCDataModel::assign(const std::string& location, const Data& data) {
void JSCDataModel::init(const Element<std::string>& dataElem,
const Node<std::string>& node,
const std::string& content) {
+ TIME_BLOCK
try {
assign(dataElem, node, content);
} catch (Event e) {
@@ -713,6 +740,7 @@ void JSCDataModel::init(const Element<std::string>& dataElem,
}
void JSCDataModel::init(const std::string& location, const Data& data) {
+ TIME_BLOCK
try {
assign(location, data);
} catch (Event e) {
@@ -723,6 +751,7 @@ void JSCDataModel::init(const std::string& location, const Data& data) {
}
std::string JSCDataModel::andExpressions(std::list<std::string> expressions) {
+ TIME_BLOCK
if (expressions.size() == 0)
return "";
diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
index 944d185..9bc39d6 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
+++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
@@ -120,6 +120,7 @@ protected:
Event _event;
JSGlobalContextRef _ctx;
+
};
#ifdef BUILD_AS_PLUGINS
diff --git a/src/uscxml/transform/ChartToC.cpp b/src/uscxml/transform/ChartToC.cpp
index 574478d..02c1e91 100644
--- a/src/uscxml/transform/ChartToC.cpp
+++ b/src/uscxml/transform/ChartToC.cpp
@@ -48,6 +48,7 @@ ChartToC::ChartToC(const Interpreter& other) : TransformerImpl() {
void ChartToC::writeTo(std::ostream& stream) {
_binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY);
+ _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : "");
std::set<std::string> elements;
elements.insert(_nsInfo.xmlNSPrefix + "scxml");
@@ -121,6 +122,10 @@ void ChartToC::writeMacros(std::ostream& stream) {
stream << "#define CLEARBIT(idx, bitset) bitset[idx >> 3] &= (1 << (idx & 7)) ^ 0xFF;" << std::endl;
stream << std::endl;
+ stream << "#define likely(x) (__builtin_expect (!!(x), 1))" << std::endl;
+ stream << "#define unlikely(x) (__builtin_expect (!!(x), 0))" << std::endl;
+ stream << std::endl;
+
stream << "// error return codes" << std::endl;
stream << "#define SCXML_ERR_OK 0" << std::endl;
stream << "#define SCXML_ERR_IDLE 1" << std::endl;
@@ -130,8 +135,10 @@ void ChartToC::writeMacros(std::ostream& stream) {
stream << "#define SCXML_ERR_EXEC_CONTENT 5" << std::endl;
stream << "#define SCXML_ERR_INVALID_TARGET 6" << std::endl;
stream << "#define SCXML_ERR_INVALID_TYPE 7" << std::endl;
+ stream << "#define SCXML_ERR_UNSUPPORTED 8" << std::endl;
stream << std::endl;
+ stream << "#define SCXML_MACHINE_NAME \"" << _name << "\"" << std::endl;
stream << "#define SCXML_NUMBER_STATES " << _states.size() << std::endl;
stream << "#define SCXML_NUMBER_TRANSITIONS " << _transitions.size() << std::endl;
stream << std::endl;
@@ -139,6 +146,7 @@ void ChartToC::writeMacros(std::ostream& stream) {
stream << "#define SCXML_TRANS_SPONTANEOUS 0x01" << std::endl;
stream << "#define SCXML_TRANS_TARGETLESS 0x02" << std::endl;
stream << "#define SCXML_TRANS_INTERNAL 0x04" << std::endl;
+ stream << "#define SCXML_TRANS_HISTORY 0x08" << std::endl;
stream << std::endl;
stream << "#define SCXML_STATE_ATOMIC 0x01" << std::endl;
@@ -158,6 +166,7 @@ void ChartToC::writeMacros(std::ostream& stream) {
stream << std::endl;
stream << "#define ELEM_DATA_IS_SET(data) (data->id != NULL)" << std::endl;
+ stream << "#define ELEM_DONEDATA_IS_SET(donedata) (donedata->content != NULL || donedata->contentexpr != NULL || donedata->params != NULL)" << std::endl;
stream << "#define ELEM_PARAM_IS_SET(param) (param->name != NULL)" << std::endl;
stream << std::endl;
}
@@ -175,6 +184,7 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << "typedef struct scxml_elem_send scxml_elem_send;" << std::endl;
stream << "typedef struct scxml_elem_param scxml_elem_param;" << std::endl;
stream << "typedef struct scxml_elem_data scxml_elem_data;" << std::endl;
+ stream << "typedef struct scxml_elem_donedata scxml_elem_donedata;" << std::endl;
stream << "typedef struct scxml_elem_foreach scxml_elem_foreach;" << std::endl;
stream << std::endl;
@@ -183,7 +193,7 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << "typedef int (*is_enabled_cb_t)(const scxml_ctx* ctx, const scxml_transition* transition, const void* event);" << std::endl;
stream << "typedef int (*is_true_cb_t)(const scxml_ctx* ctx, const char* expr);" << std::endl;
stream << "typedef int (*exec_content_t)(const scxml_ctx* ctx, const scxml_state* state, const void* event);" << std::endl;
- stream << "typedef int (*raise_done_event_t)(const scxml_ctx* ctx, const scxml_state* state);" << std::endl;
+ stream << "typedef int (*raise_done_event_t)(const scxml_ctx* ctx, const scxml_state* state, const scxml_elem_donedata* donedata);" << std::endl;
stream << "typedef int (*invoke_t)(const scxml_ctx* ctx, const scxml_state* s, const scxml_invoke* x);" << std::endl;
stream << std::endl;
@@ -210,6 +220,7 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << "struct scxml_state {" << std::endl;
stream << " const char* name; // eventual name" << std::endl;
+ stream << " uint16_t source; // parent" << std::endl;
stream << " exec_content_t on_entry; // on entry handlers" << std::endl;
stream << " exec_content_t on_exit; // on exit handlers" << std::endl;
stream << " invoke_t invoke; // invocations" << std::endl;
@@ -247,6 +258,14 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << "};" << std::endl;
stream << std::endl;
+ stream << "struct scxml_elem_donedata {" << std::endl;
+ stream << " uint16_t source;" << std::endl;
+ stream << " const char* content;" << std::endl;
+ stream << " const char* contentexpr;" << std::endl;
+ stream << " const scxml_elem_param* params;" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
stream << "struct scxml_elem_invoke {" << std::endl;
stream << " const char* type;" << std::endl;
stream << " const char* typeexpr;" << std::endl;
@@ -259,6 +278,7 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << " const scxml_elem_param* params;" << std::endl;
stream << " const exec_content_finalize_t* finalize;" << std::endl;
stream << " const char* content;" << std::endl;
+ stream << " const char* contentexpr;" << std::endl;
stream << " void* user_data;" << std::endl;
stream << "};" << std::endl;
stream << std::endl;
@@ -276,6 +296,7 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << " const char* delayexpr;" << std::endl;
stream << " const char* namelist;" << std::endl;
stream << " const char* content;" << std::endl;
+ stream << " const char* contentexpr;" << std::endl;
stream << " const scxml_elem_param* params;" << std::endl;
stream << " void* user_data;" << std::endl;
stream << "};" << std::endl;
@@ -314,7 +335,7 @@ void ChartToC::writeTypes(std::ostream& stream) {
void ChartToC::writeHelpers(std::ostream& stream) {
stream << "#ifdef SCXML_VERBOSE" << std::endl;
- stream << "void printStateNames(const char* a) {" << std::endl;
+ stream << "static void printStateNames(const char* a) {" << std::endl;
stream << " const char* seperator = \"\";" << std::endl;
stream << " for (int i = 0; i < SCXML_NUMBER_STATES; i++) {" << std::endl;
stream << " if (IS_SET(i, a)) {" << std::endl;
@@ -326,7 +347,7 @@ void ChartToC::writeHelpers(std::ostream& stream) {
stream << "}" << std::endl;
stream << std::endl;
- stream << "void printBitsetIndices(const char* a, size_t length) {" << std::endl;
+ stream << "static void printBitsetIndices(const char* a, size_t length) {" << std::endl;
stream << " const char* seperator = \"\";" << std::endl;
stream << " for (int i = 0; i < length; i++) {" << std::endl;
stream << " if (IS_SET(i, a)) {" << std::endl;
@@ -340,41 +361,48 @@ void ChartToC::writeHelpers(std::ostream& stream) {
stream << "#endif" << std::endl;
stream << std::endl;
- stream << "void bit_or(char* dest, const char* mask, size_t length) {" << std::endl;
- stream << " for (int i = 0; i < length; ++i) {" << std::endl;
- stream << " dest[i] |= mask[i];" << std::endl;
- stream << " }" << std::endl;
+ stream << "static void bit_or(char* dest, const char* mask, size_t i) {" << std::endl;
+ stream << " do {" << std::endl;
+ stream << " dest[i - 1] |= mask[i - 1];" << std::endl;
+ stream << " } while(--i);" << std::endl;
stream << "}" << std::endl;
stream << std::endl;
- stream << "void bit_copy(char* dest, const char* source, size_t length) {" << std::endl;
- stream << " for (int i = 0; i < length; ++i) {" << std::endl;
- stream << " dest[i] = source[i];" << std::endl;
- stream << " }" << std::endl;
+ stream << "static void bit_copy(char* dest, const char* source, size_t i) {" << std::endl;
+ stream << " do {" << std::endl;
+ stream << " dest[i - 1] = source[i - 1];" << std::endl;
+ stream << " } while(--i);" << std::endl;
stream << "}" << std::endl;
stream << std::endl;
- stream << "int bit_has_and(const char* a, const char* b, size_t length) {" << std::endl;
- stream << " for (int i = 0; i < length; ++i) {" << std::endl;
- stream << " if (a[i] & b[i])" << std::endl;
+ stream << "static int bit_has_and(const char* a, const char* b, size_t i) {" << std::endl;
+ stream << " do {" << std::endl;
+ stream << " if (a[i - 1] & b[i - 1])" << std::endl;
stream << " return true;" << std::endl;
- stream << " }" << std::endl;
+ stream << " } while(--i);" << std::endl;
stream << " return false;" << std::endl;
stream << "}" << std::endl;
stream << std::endl;
- stream << "void bit_and_not(char* dest, const char* mask, size_t length) {" << std::endl;
- stream << " for (int i = 0; i < length; ++i) {" << std::endl;
- stream << " dest[i] &= ~mask[i];" << std::endl;
- stream << " }" << std::endl;
+ stream << "static void bit_and_not(char* dest, const char* mask, size_t i) {" << std::endl;
+ stream << " do {" << std::endl;
+ stream << " dest[i - 1] &= ~mask[i - 1];" << std::endl;
+ stream << " } while(--i);" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+
+ stream << "static void bit_and(char* dest, const char* mask, size_t i) {" << std::endl;
+ stream << " do {" << std::endl;
+ stream << " dest[i - 1] &= mask[i - 1];" << std::endl;
+ stream << " } while(--i);" << std::endl;
stream << "}" << std::endl;
stream << std::endl;
- stream << "int bit_any_set(const char* a, size_t length) {" << std::endl;
- stream << " for (int i = 0; i < length; ++i) {" << std::endl;
- stream << " if (a[i] > 0)" << std::endl;
+ stream << "static int bit_any_set(const char* a, size_t i) {" << std::endl;
+ stream << " do {" << std::endl;
+ stream << " if (a[i - 1] > 0)" << std::endl;
stream << " return true;" << std::endl;
- stream << " }" << std::endl;
+ stream << " } while(--i);" << std::endl;
stream << " return false;" << std::endl;
stream << "}" << std::endl;
stream << std::endl;
@@ -385,9 +413,34 @@ void ChartToC::writeExecContent(std::ostream& stream) {
for (int i = 0; i < _states.size(); i++) {
Element<std::string> state(_states[i]);
+ if (i == 0) {
+ // root state - we need to perform some initialization here
+ NodeSet<std::string> globalScripts = filterChildElements(_nsInfo.xmlNSPrefix + "script", state);
+ if (globalScripts.size() > 0) {
+ _hasGlobalScripts = true;
+ for (int j = 0; j < globalScripts.size(); j++) {
+ stream << "static int global_script_" << toStr(j) << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << " int err = SCXML_ERR_OK;" << std::endl;
+ writeExecContent(stream, globalScripts[j], 1);
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ }
+
+ stream << "static int global_script(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ for (int j = 0; j < globalScripts.size(); j++) {
+ stream << " global_script_" << toStr(j) << "(ctx, state, event);" << std::endl;
+ }
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+ } else {
+ _hasGlobalScripts = false;
+ }
+ }
+
NodeSet<std::string> onexit = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", state);
for (int j = 0; j < onexit.size(); j++) {
- stream << "int " << DOMUtils::idForNode(state) << "_on_exit_" << toStr(j) << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << "static int " << DOMUtils::idForNode(state) << "_on_exit_" << toStr(j) << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
stream << " int err = SCXML_ERR_OK;" << std::endl;
writeExecContent(stream, onexit[j], 1);
stream << " return SCXML_ERR_OK;" << std::endl;
@@ -396,7 +449,7 @@ void ChartToC::writeExecContent(std::ostream& stream) {
}
if (onexit.size() > 0) {
- stream << "int " << DOMUtils::idForNode(state) << "_on_exit(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << "static int " << DOMUtils::idForNode(state) << "_on_exit(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
for (int j = 0; j < onexit.size(); j++) {
stream << " " << DOMUtils::idForNode(state) << "_on_exit_" << toStr(j) << "(ctx, state, event);" << std::endl;
}
@@ -408,7 +461,7 @@ void ChartToC::writeExecContent(std::ostream& stream) {
NodeSet<std::string> onentry = filterChildElements(_nsInfo.xmlNSPrefix + "onentry", state);
for (int j = 0; j < onentry.size(); j++) {
- stream << "int " << DOMUtils::idForNode(state) << "_on_entry_" << toStr(j) << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << "static int " << DOMUtils::idForNode(state) << "_on_entry_" << toStr(j) << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
stream << " int err = SCXML_ERR_OK;" << std::endl;
writeExecContent(stream, onentry[j], 1);
stream << " return SCXML_ERR_OK;" << std::endl;
@@ -422,7 +475,7 @@ void ChartToC::writeExecContent(std::ostream& stream) {
NodeSet<std::string> initialTransition = filterChildElements(_nsInfo.xmlNSPrefix + "transition", initial);
if (initialTransition.size() > 0) {
hasInitialState = true;
- stream << "int " << DOMUtils::idForNode(state) << "_initial" << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << "static int " << DOMUtils::idForNode(state) << "_initial" << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
stream << " int err = SCXML_ERR_OK;" << std::endl;
writeExecContent(stream, initialTransition[0], 1);
stream << " return SCXML_ERR_OK;" << std::endl;
@@ -433,7 +486,7 @@ void ChartToC::writeExecContent(std::ostream& stream) {
if (onentry.size() > 0) {
- stream << "int " << DOMUtils::idForNode(state) << "_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << "static int " << DOMUtils::idForNode(state) << "_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
for (int j = 0; j < onentry.size(); j++) {
stream << " " << DOMUtils::idForNode(state) << "_on_entry_" << toStr(j) << "(ctx, state, event);" << std::endl;
}
@@ -449,7 +502,7 @@ void ChartToC::writeExecContent(std::ostream& stream) {
NodeSet<std::string> invoke = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", state);
if (invoke.size() > 0) {
- stream << "int " << DOMUtils::idForNode(state) << "_invoke(const scxml_ctx* ctx, const scxml_state* s, const scxml_invoke* x) {" << std::endl;
+ stream << "static int " << DOMUtils::idForNode(state) << "_invoke(const scxml_ctx* ctx, const scxml_state* s, const scxml_invoke* x) {" << std::endl;
for (int j = 0; j < invoke.size(); j++) {
stream << " ctx->invoke(ctx, s, x);" << std::endl;
stream << " return SCXML_ERR_OK;" << std::endl;
@@ -467,9 +520,11 @@ void ChartToC::writeExecContent(std::ostream& stream) {
NodeSet<std::string> execContent = filterChildType(Node_base::ELEMENT_NODE, transition);
if (execContent.size() > 0) {
- stream << "int " << DOMUtils::idForNode(transition) << "_on_trans(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << "static int " << DOMUtils::idForNode(transition) << "_on_trans(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
stream << " int err = SCXML_ERR_OK;" << std::endl;
- writeExecContent(stream, Element<std::string>(execContent[0]), 1);
+ for (int j = 0; j < execContent.size(); j++) {
+ writeExecContent(stream, Element<std::string>(execContent[j]), 1);
+ }
stream << " return SCXML_ERR_OK;" << std::endl;
stream << "}" << std::endl;
stream << std::endl;
@@ -510,9 +565,9 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
}
} else if(TAGNAME(elem) == "script") {
stream << padding;
- stream << "if (ctx->exec_content_script != NULL) {" << std::endl;
+ stream << "if likely(ctx->exec_content_script != NULL) {" << std::endl;
stream << padding;
- stream << " if ((err = ctx->exec_content_script(ctx, ";
+ stream << " if unlikely((err = ctx->exec_content_script(ctx, ";
stream << (HAS_ATTR(elem, "src") ? "\"" + escape(ATTR(elem, "src")) + "\"" : "NULL") << ", ";
NodeSet<std::string> scriptTexts = filterChildType(Node_base::TEXT_NODE, elem);
@@ -524,16 +579,16 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
stream << "NULL";
}
- stream << ")) != SCXML_ERR_OK) return err" << std::endl;
+ stream << ")) != SCXML_ERR_OK) return err;" << std::endl;
stream << padding << "} else {" << std::endl;
stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
stream << padding << "}" << std::endl;
} else if(TAGNAME(elem) == "log") {
stream << padding;
- stream << "if (ctx->exec_content_log != NULL) {" << std::endl;
+ stream << "if likely(ctx->exec_content_log != NULL) {" << std::endl;
stream << padding;
- stream << " if ((ctx->exec_content_log(ctx, ";
+ stream << " if unlikely((ctx->exec_content_log(ctx, ";
stream << (HAS_ATTR(elem, "label") ? "\"" + escape(ATTR(elem, "label")) + "\"" : "NULL") << ", ";
stream << (HAS_ATTR(elem, "expr") ? "\"" + escape(ATTR(elem, "expr")) + "\"" : "NULL");
stream << ")) != SCXML_ERR_OK) return err;" << std::endl;
@@ -542,12 +597,12 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
stream << padding << "}" << std::endl;
} else if(TAGNAME(elem) == "foreach") {
- stream << padding << "if (ctx->exec_content_foreach_init != NULL &&" << std::endl;
- stream << padding << " ctx->exec_content_foreach_next != NULL &&" << std::endl;
- stream << padding << " ctx->exec_content_foreach_done != NULL) {" << std::endl;
+ stream << padding << "if likely(ctx->exec_content_foreach_init != NULL &&" << std::endl;
+ stream << padding << " ctx->exec_content_foreach_next != NULL &&" << std::endl;
+ stream << padding << " ctx->exec_content_foreach_done != NULL) {" << std::endl;
stream << std::endl;
- stream << padding << " if ((ctx->exec_content_foreach_init(ctx, &scxml_elem_foreachs[" << ATTR(elem, "documentOrder") << "])) != SCXML_ERR_OK) return err;" << std::endl;
+ stream << padding << " if unlikely((ctx->exec_content_foreach_init(ctx, &scxml_elem_foreachs[" << ATTR(elem, "documentOrder") << "])) != SCXML_ERR_OK) return err;" << std::endl;
stream << padding << " while (ctx->exec_content_foreach_next(ctx, &scxml_elem_foreachs[" << ATTR(elem, "documentOrder") << "]) == SCXML_ERR_OK) {" << std::endl;
Arabica::DOM::Node<std::string> child = node.getFirstChild();
while(child) {
@@ -562,7 +617,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
} else if(TAGNAME(elem) == "if") {
stream << padding;
- stream << "if (ctx->is_true != NULL) {" << std::endl;
+ stream << "if likely(ctx->is_true != NULL) {" << std::endl;
stream << padding;
stream << " if (ctx->is_true(ctx, " << (HAS_ATTR(elem, "cond") ? "\"" + escape(ATTR(elem, "cond")) + "\"" : "NULL") << ")) {" << std::endl;
Arabica::DOM::Node<std::string> child = elem.getFirstChild();
@@ -585,7 +640,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
} else if(TAGNAME(elem) == "assign") {
stream << padding;
- stream << "if (ctx->exec_content_assign != NULL) {" << std::endl;
+ stream << "if likely(ctx->exec_content_assign != NULL) {" << std::endl;
stream << padding;
stream << " if ((ctx->exec_content_assign(ctx, ";
stream << (HAS_ATTR(elem, "location") ? "\"" + escape(ATTR(elem, "location")) + "\"" : "NULL") << ", ";
@@ -609,7 +664,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
} else if(TAGNAME(elem) == "raise") {
stream << padding;
- stream << "if (ctx->exec_content_raise != NULL) {" << std::endl;
+ stream << "if likely(ctx->exec_content_raise != NULL) {" << std::endl;
stream << padding;
stream << " if ((ctx->exec_content_raise(ctx, ";
stream << (HAS_ATTR(elem, "event") ? "\"" + escape(ATTR(elem, "event")) + "\"" : "NULL");
@@ -620,7 +675,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
} else if(TAGNAME(elem) == "send") {
stream << padding;
- stream << "if (ctx->exec_content_send != NULL) {" << std::endl;
+ stream << "if likely(ctx->exec_content_send != NULL) {" << std::endl;
stream << padding;
stream << " if ((ctx->exec_content_send(ctx, &scxml_elem_sends[" << ATTR(elem, "documentOrder") << "]";
stream << ")) != SCXML_ERR_OK) return err;" << std::endl;
@@ -630,7 +685,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
} else if(TAGNAME(elem) == "cancel") {
stream << padding;
- stream << "if (ctx->exec_content_cancel != NULL) {" << std::endl;
+ stream << "if likely(ctx->exec_content_cancel != NULL) {" << std::endl;
stream << padding;
stream << " if ((ctx->exec_content_cancel(ctx, ";
stream << (HAS_ATTR(elem, "sendid") ? "\"" + escape(ATTR(elem, "sendid")) + "\"" : "NULL") << ", ";
@@ -650,7 +705,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
void ChartToC::writeElementInfo(std::ostream& stream) {
NodeSet<std::string> foreachs = filterChildElements(_nsInfo.xmlNSPrefix + "foreach", _scxml, true);
if (foreachs.size() > 0) {
- stream << "scxml_elem_foreach scxml_elem_foreachs[" << foreachs.size() << "] = {" << std::endl;
+ stream << "static scxml_elem_foreach scxml_elem_foreachs[" << foreachs.size() << "] = {" << std::endl;
for (int i = 0; i < foreachs.size(); i++) {
Element<std::string> foreach(foreachs[i]);
stream << " { ";
@@ -663,32 +718,37 @@ void ChartToC::writeElementInfo(std::ostream& stream) {
stream << "};" << std::endl;
stream << std::endl;
}
-
+
NodeSet<std::string> datas = filterChildElements(_nsInfo.xmlNSPrefix + "data", _scxml, true);
if (datas.size() > 0) {
- if (_binding == InterpreterImpl::EARLY) {
- Element<std::string>(_states[0]).setAttribute("dataIndex", "0");
- }
-
+ size_t dataIndexOffset = 0;
Node<std::string> parent;
size_t distinctParents = 0;
- for (int i = 0; i < datas.size(); i++) {
- Element<std::string> data(datas[i]);
- if (data.getParentNode() != parent) {
- distinctParents++;
+
+ if (_binding == InterpreterImpl::EARLY) {
+ Element<std::string>(_states[0]).setAttribute("dataIndex", "0");
+ distinctParents = 1;
+ } else {
+ for (int i = 0; i < datas.size(); i++) {
+ Element<std::string> data(datas[i]);
+ if (data.getParentNode() != parent) {
+ distinctParents++;
+ }
}
}
+
parent = Node<std::string>();
- stream << "scxml_elem_data scxml_elem_datas[" << datas.size() + distinctParents << "] = {" << std::endl;
+ stream << "static scxml_elem_data scxml_elem_datas[" << datas.size() + distinctParents << "] = {" << std::endl;
for (int i = 0; i < datas.size(); i++) {
Element<std::string> data(datas[i]);
if (data.getParentNode().getParentNode() != parent) {
if (_binding == InterpreterImpl::LATE) {
- Element<std::string>(data.getParentNode().getParentNode()).setAttribute("dataIndex", toStr(i));
if (i > 0) {
stream << " { NULL, NULL, NULL, NULL }," << std::endl;
+ dataIndexOffset++;
}
+ Element<std::string>(data.getParentNode().getParentNode()).setAttribute("dataIndex", toStr(i + dataIndexOffset));
}
parent = data.getParentNode().getParentNode();
}
@@ -706,7 +766,6 @@ void ChartToC::writeElementInfo(std::ostream& stream) {
} else {
stream << "NULL";
}
-
stream << " }," << std::endl;
}
@@ -727,7 +786,7 @@ void ChartToC::writeElementInfo(std::ostream& stream) {
}
parent = Node<std::string>();
- stream << "scxml_elem_param scxml_elem_params[" << params.size() + distinctParents << "] = {" << std::endl;
+ stream << "static scxml_elem_param scxml_elem_params[" << params.size() + distinctParents << "] = {" << std::endl;
for (int i = 0; i < params.size(); i++) {
Element<std::string> param(params[i]);
if (param.getParentNode() != parent) {
@@ -751,7 +810,7 @@ void ChartToC::writeElementInfo(std::ostream& stream) {
NodeSet<std::string> sends = filterChildElements(_nsInfo.xmlNSPrefix + "send", _scxml, true);
if (sends.size() > 0) {
- stream << "scxml_elem_send scxml_elem_sends[" << sends.size() << "] = {" << std::endl;
+ stream << "static scxml_elem_send scxml_elem_sends[" << sends.size() << "] = {" << std::endl;
for (int i = 0; i < sends.size(); i++) {
Element<std::string> send(sends[i]);
stream << " { ";
@@ -770,15 +829,14 @@ void ChartToC::writeElementInfo(std::ostream& stream) {
NodeSet<std::string> contents = filterChildElements(_nsInfo.xmlNSPrefix + "content", send);
if (contents.size() > 0) {
std::stringstream ss;
- stream << "\"";
NodeList<std::string> cChilds = contents[0].getChildNodes();
for (int j = 0; j < cChilds.getLength(); j++) {
ss << cChilds.item(j);
- stream << escape(ss.str());
}
- stream << "\", ";
+ stream << (ss.str().size() > 0 ? "\"" + escape(ss.str()) + "\", " : "NULL, ");
+ stream << (HAS_ATTR_CAST(contents[0], "expr") ? "\"" + ATTR_CAST(contents[0], "expr") + "\", " : "NULL, ");
} else {
- stream << "NULL, ";
+ stream << "NULL, NULL, ";
}
@@ -796,17 +854,60 @@ void ChartToC::writeElementInfo(std::ostream& stream) {
stream << std::endl;
}
+ NodeSet<std::string> donedatas = filterChildElements(_nsInfo.xmlNSPrefix + "donedata", _scxml, true);
+ if (donedatas.size() > 0) {
+ _hasDoneData = true;
+ stream << "static scxml_elem_donedata scxml_elem_donedatas[" << donedatas.size() + 1 << "] = {" << std::endl;
+ for (int i = 0; i < donedatas.size(); i++) {
+ Element<std::string> donedata(donedatas[i]);
+ stream << " { ";
+
+ // parent
+ stream << ATTR_CAST(donedata.getParentNode(), "documentOrder") << ", ";
+
+ NodeSet<std::string> contents = filterChildElements(_nsInfo.xmlNSPrefix + "content", donedata);
+ if (contents.size() > 0) {
+ std::stringstream ss;
+ NodeList<std::string> cChilds = contents[0].getChildNodes();
+ for (int j = 0; j < cChilds.getLength(); j++) {
+ ss << cChilds.item(j);
+ }
+ stream << (ss.str().size() > 0 ? "\"" + escape(ss.str()) + "\", " : "NULL, ");
+ stream << (HAS_ATTR_CAST(contents[0], "expr") ? "\"" + ATTR_CAST(contents[0], "expr") + "\", " : "NULL, ");
+ } else {
+ stream << "NULL, NULL, ";
+ }
+
+ if (HAS_ATTR(donedata, "paramIndex")) {
+ stream << "(const scxml_elem_param*)&scxml_elem_params[" << escape(ATTR(donedata, "paramIndex")) << "]";
+ } else {
+ stream << "NULL";
+ }
+
+ stream << " }," << std::endl;
+ donedata.setAttribute("documentOrder", toStr(i));
+ }
+ stream << " { 0, NULL, NULL }" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+ } else {
+ _hasDoneData = false;
+ }
+
}
void ChartToC::writeStates(std::ostream& stream) {
- stream << "scxml_state scxml_states[" << toStr(_states.size()) << "] = {" << std::endl;
+ stream << "static scxml_state scxml_states[" << toStr(_states.size()) << "] = {" << std::endl;
for (int i = 0; i < _states.size(); i++) {
Element<std::string> state(_states[i]);
stream << " { ";
// name
stream << (HAS_ATTR(state, "id") ? "\"" + escape(ATTR(state, "id")) + "\"" : "NULL") << ", ";
-
+
+ // parent
+ stream << (i == 0 ? "0" : ATTR_CAST(state.getParentNode(), "documentOrder")) << ", ";
+
// onentry
stream << (filterChildElements(_nsInfo.xmlNSPrefix + "onentry", state).size() > 0 ? DOMUtils::idForNode(state) + "_on_entry" : "NULL") << ", ";
@@ -834,7 +935,20 @@ void ChartToC::writeStates(std::ostream& stream) {
// default completion
NodeSet<std::string> completion;
- if (isParallel(state)) {
+ if (isHistory(state)) {
+ bool deep = (HAS_ATTR(state, "type") && iequals(ATTR(state, "type"), "deep"));
+ for (int j = 0; j < _states.size(); j++) {
+ if (deep) {
+ if (isDescendant(_states[j], state.getParentNode()) && !isHistory(Element<std::string>(_states[j]))) {
+ completion.push_back(_states[j]);
+ }
+ } else {
+ if (_states[j].getParentNode() == state.getParentNode() && !isHistory(Element<std::string>(_states[j]))) {
+ completion.push_back(_states[j]);
+ }
+ }
+ }
+ } if (isParallel(state)) {
completion = getChildStates(state);
} else if (state.hasAttribute("initial")) {
completion = getStates(tokenizeIdRefs(state.getAttribute("initial")));
@@ -852,6 +966,7 @@ void ChartToC::writeStates(std::ostream& stream) {
continue;
if (isState(Element<std::string>(childs.item(i)))) {
completion.push_back(childs.item(i));
+ break;
}
}
}
@@ -924,7 +1039,7 @@ void ChartToC::writeStates(std::ostream& stream) {
void ChartToC::writeTransitions(std::ostream& stream) {
- stream << "scxml_transition scxml_transitions[" << toStr(_transitions.size()) << "] = {" << std::endl;
+ stream << "static scxml_transition scxml_transitions[" << toStr(_transitions.size()) << "] = {" << std::endl;
for (int i = 0; i < _transitions.size(); i++) {
Element<std::string> transition(_transitions[i]);
stream << " { ";
@@ -986,19 +1101,24 @@ void ChartToC::writeTransitions(std::ostream& stream) {
std::string seperator = "";
if (!HAS_ATTR(transition, "target")) {
stream << seperator << "SCXML_TRANS_TARGETLESS";
- seperator = " & ";
+ seperator = " | ";
}
if (HAS_ATTR(transition, "type") && iequals(ATTR(transition, "type"), "internal")) {
stream << seperator << "SCXML_TRANS_INTERNAL";
- seperator = " & ";
+ seperator = " | ";
}
if (!HAS_ATTR(transition, "event")) {
stream << seperator << "SCXML_TRANS_SPONTANEOUS";
- seperator = " & ";
+ seperator = " | ";
}
+ if (iequals(TAGNAME_CAST(transition.getParentNode()), "history")) {
+ stream << seperator << "SCXML_TRANS_HISTORY";
+ seperator = " | ";
+ }
+
if (seperator.size() == 0) {
stream << "0";
}
@@ -1102,10 +1222,11 @@ void ChartToC::writeCharArrayInitList(std::ostream& stream, const std::string& b
}
void ChartToC::writeFSM(std::ostream& stream) {
- stream << "int scxml_step(scxml_ctx* ctx) {" << std::endl;
+ stream << "static int scxml_step(scxml_ctx* ctx) {" << std::endl;
stream << std::endl;
stream << "#ifdef SCXML_VERBOSE" << std::endl;
+ stream << " printf(\"Config: \");" << std::endl;
stream << " printStateNames(ctx->config);" << std::endl;
stream << "#endif" << std::endl;
stream << std::endl;
@@ -1127,7 +1248,10 @@ void ChartToC::writeFSM(std::ostream& stream) {
stream << std::endl;
stream << " void* event;" << std::endl;
- stream << " if (ctx->flags == SCXML_CTX_PRISTINE) {" << std::endl;
+ stream << " if unlikely(ctx->flags == SCXML_CTX_PRISTINE) {" << std::endl;
+ if (_hasGlobalScripts) {
+ stream << " global_script(ctx, &scxml_states[0], NULL);" << std::endl;
+ }
stream << " bit_or(target_set, scxml_states[0].completion, " << _stateCharArraySize << ");" << std::endl;
stream << " ctx->flags |= SCXML_CTX_SPONTANEOUS | SCXML_CTX_INITIALIZED;" << std::endl;
stream << " goto COMPLETE_CONFIG;" << std::endl;
@@ -1147,8 +1271,14 @@ void ChartToC::writeFSM(std::ostream& stream) {
stream << " }" << std::endl;
stream << std::endl;
+// HISTORY TRANSITION IS SELECTED BY ACCIDENT!
+
stream << "SELECT_TRANSITIONS:" << std::endl;
stream << " for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {" << std::endl;
+ stream << " // never select history or initial transitions automatically" << std::endl;
+ stream << " if unlikely(scxml_transitions[i].type & (SCXML_TRANS_HISTORY | SCXML_TRANS_HISTORY))" << std::endl;
+ stream << " continue;" << std::endl;
+ stream << std::endl;
stream << " // is the transition active?" << std::endl;
stream << " if (IS_SET(scxml_transitions[i].source, ctx->config)) {" << std::endl;
stream << " // is it non-conflicting?" << std::endl;
@@ -1173,37 +1303,62 @@ void ChartToC::writeFSM(std::ostream& stream) {
stream << " }" << std::endl;
stream << " }" << std::endl;
stream << " }" << std::endl;
+ stream << " bit_and(exit_set, ctx->config, " << _stateCharArraySize << ");" << std::endl;
stream << std::endl;
stream << " if (ctx->flags & SCXML_CTX_TRANSITION_FOUND) {" << std::endl;
stream << " ctx->flags |= SCXML_CTX_SPONTANEOUS;" << std::endl;
stream << " } else {" << std::endl;
stream << " ctx->flags &= ~SCXML_CTX_SPONTANEOUS;" << std::endl;
- stream << " goto MACRO_STEP;" << std::endl;
+ stream << " // goto MACRO_STEP;" << std::endl;
+ stream << " return SCXML_ERR_OK;" << std::endl;
stream << " }" << std::endl;
stream << std::endl;
+ stream << "#ifdef SCXML_VERBOSE" << std::endl;
+ stream << " printf(\"Targets: \");" << std::endl;
+ stream << " printStateNames(target_set);" << std::endl;
+ stream << "#endif" << std::endl;
+ stream << std::endl;
+
stream << "REMEMBER_HISTORY:" << std::endl;
- stream << " // are my ancestors in the exit set?" << std::endl;
stream << " for (int i = 0; i < SCXML_NUMBER_STATES; i++) {" << std::endl;
- stream << " if (IS_SET(i, ctx->config) && bit_has_and(exit_set, scxml_states[i].ancestors, " << _stateCharArraySize << ")) {" << std::endl;
- stream << " SET_BIT(i, ctx->history);" << std::endl;
+ stream << " if unlikely(scxml_states[i].type == SCXML_STATE_HISTORY_SHALLOW || scxml_states[i].type == SCXML_STATE_HISTORY_DEEP) {" << std::endl;
+ stream << " // a history state whose parent is about to be exited" << std::endl;
+ stream << " if unlikely(IS_SET(scxml_states[i].source, exit_set)) {" << std::endl;
+ stream << " char history[" << _stateCharArraySize << "] = " << _stateCharArrayInit << ";" << std::endl;
+ stream << " bit_copy(history, scxml_states[i].completion, " << _stateCharArraySize << ");" << std::endl;
+ stream << std::endl;
+ stream << " // set those states who were enabled" << std::endl;
+ stream << " bit_and(history, ctx->config, " << _stateCharArraySize << ");" << std::endl;
+ stream << std::endl;
+ stream << " // clear current history with completion mask" << std::endl;
+ stream << " bit_and_not(ctx->history, scxml_states[i].completion, " << _stateCharArraySize << ");" << std::endl;
+ stream << std::endl;
+ stream << " // set history" << std::endl;
+ stream << " bit_or(ctx->history, history, " << _stateCharArraySize << ");" << std::endl;
+ stream << " }" << std::endl;
stream << " }" << std::endl;
stream << " }" << std::endl;
- stream << std::endl;
-
+
stream << "#ifdef SCXML_VERBOSE" << std::endl;
stream << " printf(\"Exiting: \");" << std::endl;
stream << " printStateNames(exit_set);" << std::endl;
stream << "#endif" << std::endl;
stream << std::endl;
+ stream << "#ifdef SCXML_VERBOSE" << std::endl;
+ stream << " printf(\"History: \");" << std::endl;
+ stream << " printStateNames(ctx->history);" << std::endl;
+ stream << "#endif" << std::endl;
+ stream << std::endl;
+
stream << "EXIT_STATES:" << std::endl;
stream << " for (int i = SCXML_NUMBER_STATES - 1; i >= 0; i--) {" << std::endl;
stream << " if (IS_SET(i, exit_set) && IS_SET(i, ctx->config)) {" << std::endl;
stream << " // call all on exit handlers" << std::endl;
stream << " if (scxml_states[i].on_exit != NULL) {" << std::endl;
- stream << " if((err = scxml_states[i].on_exit(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)" << std::endl;
+ stream << " if unlikely((err = scxml_states[i].on_exit(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)" << std::endl;
stream << " return err;" << std::endl;
stream << " }" << std::endl;
stream << " CLEARBIT(i, ctx->config);" << std::endl;
@@ -1232,20 +1387,42 @@ void ChartToC::writeFSM(std::ostream& stream) {
stream << " bit_or(entry_set, scxml_states[i].completion, " << _stateCharArraySize << ");" << std::endl;
stream << " break;" << std::endl;
stream << " }" << std::endl;
+ stream << " case SCXML_STATE_HISTORY_SHALLOW:" << std::endl;
+ stream << " case SCXML_STATE_HISTORY_DEEP: {" << std::endl;
+ stream << " char history_targets[" << _stateCharArraySize << "] = " << _stateCharArrayInit << ";" << std::endl;
+ stream << " if (!bit_has_and(scxml_states[i].completion, ctx->history, " << _stateCharArraySize << ")) {" << std::endl;
+ stream << " // nothing set for history, look for a default transition or enter parents completion" << std::endl;
+ stream << " for (int j = 0; j < SCXML_NUMBER_TRANSITIONS; j++) {" << std::endl;
+ stream << " if unlikely(scxml_transitions[j].source == i) {" << std::endl;
+ stream << " bit_or(entry_set, scxml_transitions[j].target, " << _stateCharArraySize << ");" << std::endl;
+ stream << " SET_BIT(j, trans_set);" << std::endl;
+ stream << " break;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " // TODO: enter parents default completion here" << std::endl;
+ stream << " } else {" << std::endl;
+ stream << " bit_copy(history_targets, scxml_states[i].completion, " << _stateCharArraySize << ");" << std::endl;
+ stream << " bit_and(history_targets, ctx->history, " << _stateCharArraySize << ");" << std::endl;
+ stream << " bit_or(entry_set, history_targets, " << _stateCharArraySize << ");" << std::endl;
+ stream << " }" << std::endl;
+ stream << " break;" << std::endl;
+ stream << " }" << std::endl;
stream << " case SCXML_STATE_INITIAL: {" << std::endl;
stream << " for (int j = 0; j < SCXML_NUMBER_TRANSITIONS; j++) {" << std::endl;
stream << " if (scxml_transitions[j].source == i) {" << std::endl;
stream << " SET_BIT(j, trans_set);" << std::endl;
stream << " CLEARBIT(i, entry_set);" << std::endl;
- stream << " bit_or(entry_set, scxml_transitions[j].target, " << _transCharArraySize << ");" << std::endl;
+ stream << " bit_or(entry_set, scxml_transitions[j].target, " << _stateCharArraySize << ");" << std::endl;
stream << " // one target may have been above, reestablish completion" << std::endl;
- stream << " goto ADD_DESCENDANTS;" << std::endl;
+ stream << " // goto ADD_DESCENDANTS; // initial will have to be first!" << std::endl;
stream << " }" << std::endl;
stream << " }" << std::endl;
stream << " break;" << std::endl;
stream << " }" << std::endl;
stream << " case SCXML_STATE_COMPOUND: { // we need to check whether one child is already in entry_set" << std::endl;
- stream << " if (!bit_has_and(entry_set, scxml_states[i].children, " << _stateCharArraySize << ")) {" << std::endl;
+ stream << " if (!bit_has_and(entry_set, scxml_states[i].children, " << _stateCharArraySize << ") &&" << std::endl;
+ stream << " !bit_has_and(ctx->config, scxml_states[i].children, " << _stateCharArraySize << "))" << std::endl;
+ stream << " {" << std::endl;
stream << " bit_or(entry_set, scxml_states[i].completion, " << _stateCharArraySize << ");" << std::endl;
stream << " }" << std::endl;
stream << " break;" << std::endl;
@@ -1263,10 +1440,10 @@ void ChartToC::writeFSM(std::ostream& stream) {
stream << "TAKE_TRANSITIONS:" << std::endl;
stream << " for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {" << std::endl;
- stream << " if (IS_SET(i, trans_set)) {" << std::endl;
+ stream << " if (IS_SET(i, trans_set) && (scxml_transitions[i].type & SCXML_TRANS_HISTORY) == 0) {" << std::endl;
stream << " // call executable content in transition" << std::endl;
stream << " if (scxml_transitions[i].on_transition != NULL) {" << std::endl;
- stream << " if((err = scxml_transitions[i].on_transition(ctx," << std::endl;
+ stream << " if unlikely((err = scxml_transitions[i].on_transition(ctx," << std::endl;
stream << " &scxml_states[scxml_transitions[i].source]," << std::endl;
stream << " event)) != SCXML_ERR_OK)" << std::endl;
stream << " return err;" << std::endl;
@@ -1284,41 +1461,57 @@ void ChartToC::writeFSM(std::ostream& stream) {
stream << "ENTER_STATES:" << std::endl;
stream << " for (int i = 0; i < SCXML_NUMBER_STATES; i++) {" << std::endl;
stream << " if (IS_SET(i, entry_set) && !IS_SET(i, ctx->config)) {" << std::endl;
+ stream << " // these are no proper states" << std::endl;
+ stream << " if unlikely(scxml_states[i].type == SCXML_STATE_HISTORY_DEEP ||" << std::endl;
+ stream << " scxml_states[i].type == SCXML_STATE_HISTORY_SHALLOW ||" << std::endl;
+ stream << " scxml_states[i].type == SCXML_STATE_INITIAL)" << std::endl;
+ stream << " continue;" << std::endl;
+ stream << std::endl;
+
stream << " SET_BIT(i, ctx->config);" << std::endl;
- stream << " if (scxml_states[i].on_entry != NULL) {" << std::endl;
- stream << " if((err = scxml_states[i].on_entry(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)" << std::endl;
- stream << " return err;" << std::endl;
- stream << " }" << std::endl;
stream << std::endl;
stream << " // initialize data" << std::endl;
- stream << " if(!IS_SET(i, ctx->initialized_data)) {" << std::endl;
- stream << " if (scxml_states[i].data != NULL && ctx->exec_content_init != NULL) {" << std::endl;
+ stream << " if (!IS_SET(i, ctx->initialized_data)) {" << std::endl;
+ stream << " if unlikely(scxml_states[i].data != NULL && ctx->exec_content_init != NULL) {" << std::endl;
stream << " ctx->exec_content_init(ctx, scxml_states[i].data);" << std::endl;
stream << " }" << std::endl;
stream << " SET_BIT(i, ctx->initialized_data);" << std::endl;
stream << " }" << std::endl;
stream << std::endl;
+ stream << " if (scxml_states[i].on_entry != NULL) {" << std::endl;
+ stream << " if unlikely((err = scxml_states[i].on_entry(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)" << std::endl;
+ stream << " return err;" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
stream << " // handle final states" << std::endl;
- stream << " if (scxml_states[i].type == SCXML_STATE_FINAL) {" << std::endl;
- stream << " if (scxml_states[i].ancestors[0] == 0x01) {" << std::endl;
+ stream << " if unlikely(scxml_states[i].type == SCXML_STATE_FINAL) {" << std::endl;
+ stream << " if unlikely(scxml_states[i].ancestors[0] == 0x01) {" << std::endl;
stream << " ctx->flags |= SCXML_CTX_TOP_LEVEL_FINAL;" << std::endl;
stream << " } else {" << std::endl;
stream << " // raise done event" << std::endl;
stream << " size_t parent = 0;" << std::endl;
- stream << " for (int j = 0; j < SCXML_NUMBER_STATES; j++) {" << std::endl;
+ stream << " for (int j = SCXML_NUMBER_STATES - 1; j >= 0; j--) {" << std::endl;
stream << " // we could trade runtime for memory here by saving the parent index" << std::endl;
- stream << " if (!IS_SET(j, scxml_states[i].ancestors)) {" << std::endl;
- stream << " if (parent != 0) {" << std::endl;
- stream << " break;" << std::endl;
- stream << " }" << std::endl;
- stream << " continue;" << std::endl;
- stream << " } else {" << std::endl;
- stream << " parent = j;" << std::endl;
+ stream << " if unlikely(IS_SET(j, scxml_states[i].ancestors)) {" << std::endl;
+ stream << " parent = j;" << std::endl;
+ stream << " break;" << std::endl;
stream << " }" << std::endl;
stream << " }" << std::endl;
- stream << " ctx->raise_done_event(ctx, &scxml_states[parent]);" << std::endl;
+ stream << " // is this raised for toplevel final as well?" << std::endl;
+ if (_hasDoneData) {
+ stream << " scxml_elem_donedata* donedata = &scxml_elem_donedatas[0];" << std::endl;
+ stream << " while(ELEM_DONEDATA_IS_SET(donedata)) {" << std::endl;
+ stream << " if unlikely(donedata->source == i)" << std::endl;
+ stream << " break;" << std::endl;
+ stream << " donedata++;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " ctx->raise_done_event(ctx, &scxml_states[parent], (ELEM_DONEDATA_IS_SET(donedata) ? donedata : NULL));" << std::endl;
+ } else {
+ stream << " ctx->raise_done_event(ctx, &scxml_states[parent], NULL);" << std::endl;
+ }
stream << " }" << std::endl;
stream << std::endl;
@@ -1330,20 +1523,20 @@ void ChartToC::writeFSM(std::ostream& stream) {
stream << " * 4. If a state remains, not all children of a parallel are final" << std::endl;
stream << " */" << std::endl;
stream << " for (int j = 0; j < SCXML_NUMBER_STATES; j++) {" << std::endl;
- stream << " if (scxml_states[j].type == SCXML_STATE_PARALLEL) {" << std::endl;
- stream << " char parallel_children[2] = {0, 0};" << std::endl;
+ stream << " if unlikely(scxml_states[j].type == SCXML_STATE_PARALLEL) {" << std::endl;
+ stream << " char parallel_children[" << _stateCharArraySize << "] = " << _stateCharArrayInit << ";" << std::endl;
stream << " size_t parallel = j;" << std::endl;
stream << " for (int k = 0; k < SCXML_NUMBER_STATES; k++) {" << std::endl;
- stream << " if (IS_SET(parallel, scxml_states[k].ancestors) && IS_SET(k, ctx->config)) {" << std::endl;
+ stream << " if unlikely(IS_SET(parallel, scxml_states[k].ancestors) && IS_SET(k, ctx->config)) {" << std::endl;
stream << " if (scxml_states[k].type == SCXML_STATE_FINAL) {" << std::endl;
- stream << " bit_and_not(parallel_children, scxml_states[k].ancestors, 2);" << std::endl;
+ stream << " bit_and_not(parallel_children, scxml_states[k].ancestors, " << _stateCharArraySize << ");" << std::endl;
stream << " } else {" << std::endl;
stream << " SET_BIT(k, parallel_children);" << std::endl;
stream << " }" << std::endl;
stream << " }" << std::endl;
stream << " }" << std::endl;
- stream << " if (!bit_any_set(parallel_children, 2)) {" << std::endl;
- stream << " ctx->raise_done_event(ctx, &scxml_states[parallel]);" << std::endl;
+ stream << " if unlikely(!bit_any_set(parallel_children, " << _stateCharArraySize << ")) {" << std::endl;
+ stream << " ctx->raise_done_event(ctx, &scxml_states[parallel], NULL);" << std::endl;
stream << " }" << std::endl;
stream << " }" << std::endl;
stream << " }" << std::endl;
@@ -1356,6 +1549,21 @@ void ChartToC::writeFSM(std::ostream& stream) {
stream << " }" << std::endl;
stream << std::endl;
+ stream << "HISTORY_TRANSITIONS:" << std::endl;
+ stream << " for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {" << std::endl;
+ stream << " if unlikely(IS_SET(i, trans_set) && (scxml_transitions[i].type & SCXML_TRANS_HISTORY)) {" << std::endl;
+ stream << " // call executable content in transition" << std::endl;
+ stream << " if (scxml_transitions[i].on_transition != NULL) {" << std::endl;
+ stream << " if unlikely((err = scxml_transitions[i].on_transition(ctx," << std::endl;
+ stream << " &scxml_states[scxml_transitions[i].source]," << std::endl;
+ stream << " event)) != SCXML_ERR_OK)" << std::endl;
+ stream << " return err;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+
stream << " return SCXML_ERR_OK;" << std::endl;
stream << "}" << std::endl;
stream << std::endl;
@@ -1372,14 +1580,14 @@ void ChartToC::inPostFixOrder(const std::set<std::string>& elements, const Eleme
for (int i = 0; i < children.getLength(); i++) {
if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE)
continue;
- const Arabica::DOM::Element<std::string>& childElem(children.item(i));
+ Arabica::DOM::Element<std::string> childElem(children.item(i));
inPostFixOrder(elements, childElem, nodes);
}
for (int i = 0; i < children.getLength(); i++) {
if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE)
continue;
- const Arabica::DOM::Element<std::string>& childElem(children.item(i));
+ Arabica::DOM::Element<std::string> childElem(children.item(i));
if (elements.find(TAGNAME(childElem)) != elements.end()) {
nodes.push_back(childElem);
@@ -1402,7 +1610,7 @@ void ChartToC::inDocumentOrder(const std::set<std::string>& elements, const Elem
for (int i = 0; i < children.getLength(); i++) {
if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE)
continue;
- const Arabica::DOM::Element<std::string>& childElem(children.item(i));
+ Arabica::DOM::Element<std::string> childElem(children.item(i));
inDocumentOrder(elements, childElem, nodes);
}
}
diff --git a/src/uscxml/transform/ChartToC.h b/src/uscxml/transform/ChartToC.h
index e18dfab..0ee0d3c 100644
--- a/src/uscxml/transform/ChartToC.h
+++ b/src/uscxml/transform/ChartToC.h
@@ -77,6 +77,9 @@ protected:
std::map<std::string, Arabica::DOM::Element<std::string> > _stateNames;
Arabica::XPath::NodeSet<std::string> _transitions;
+ bool _hasGlobalScripts;
+ bool _hasDoneData;
+
size_t _transCharArraySize;
std::string _transCharArrayInit;
diff --git a/src/uscxml/transform/ChartToMinimalSCXML.h b/src/uscxml/transform/ChartToMinimalSCXML.h
index 5975cbc..3cdab54 100644
--- a/src/uscxml/transform/ChartToMinimalSCXML.h
+++ b/src/uscxml/transform/ChartToMinimalSCXML.h
@@ -77,7 +77,6 @@ private:
public:
void operator()(ChartToMinimalSCXML* p) { /* do nothing */ }
};
- friend class ChartToMinimalSCXML;
};
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index dcb145a..5af6a4a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -154,6 +154,7 @@ if (NOT BUILD_MINIMAL)
-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
-DUSCXML_PLATFORM_ID=${USCXML_PLATFORM_ID}
-DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}
+ -DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
-DSCAFFOLDING_FOR_GENERATED_C:FILEPATH=${CMAKE_CURRENT_SOURCE_DIR}/src/test-c-machine.cpp
-P ${CMAKE_CURRENT_SOURCE_DIR}/w3c/run_generated_c_test.cmake)
diff --git a/test/ctest/CTestCustom.ctest.in b/test/ctest/CTestCustom.ctest.in
index 248ae8a..6a66839 100644
--- a/test/ctest/CTestCustom.ctest.in
+++ b/test/ctest/CTestCustom.ctest.in
@@ -198,6 +198,7 @@ set(CTEST_CUSTOM_TESTS_IGNORE
### Ignore for generated C sources
# we do not support invokers yet
+ "gen_c/ecma/test187.scxml"
"gen_c/ecma/test191.scxml"
"gen_c/ecma/test192.scxml"
"gen_c/ecma/test207.scxml"
@@ -229,9 +230,45 @@ set(CTEST_CUSTOM_TESTS_IGNORE
"gen_c/ecma/test252.scxml"
"gen_c/ecma/test253.scxml"
"gen_c/ecma/test276.scxml"
+ "gen_c/ecma/test338.scxml"
+ "gen_c/ecma/test347.scxml"
+ "gen_c/ecma/test422.scxml"
+ "gen_c/ecma/test530.scxml"
+ "gen_c/ecma/test554.scxml"
# we do not support io processors yet
"gen_c/ecma/test201.scxml"
+ "gen_c/ecma/test496.scxml"
+ "gen_c/ecma/test500.scxml"
+ "gen_c/ecma/test501.scxml"
+ "gen_c/ecma/test509.scxml"
+ "gen_c/ecma/test510.scxml"
+ "gen_c/ecma/test518.scxml"
+ "gen_c/ecma/test519.scxml"
+ "gen_c/ecma/test520.scxml"
+ "gen_c/ecma/test521.scxml"
+ "gen_c/ecma/test522.scxml"
+ "gen_c/ecma/test531.scxml"
+ "gen_c/ecma/test532.scxml"
+ "gen_c/ecma/test534.scxml"
+ "gen_c/ecma/test567.scxml"
+ "gen_c/ecma/test569.scxml"
+ "gen_c/ecma/test577.scxml"
+
+ # failing is succeeding
+ "gen_c/ecma/test301.scxml"
+
+ # manual test
+ "gen_c/ecma/test307.scxml"
+
+ # data from file
+ "gen_c/ecma/test446.scxml"
+ "gen_c/ecma/test552.scxml"
+ "gen_c/ecma/test557.scxml"
+ "gen_c/ecma/test558.scxml"
+
+ # XML DOM in data
+ "gen_c/ecma/test561.scxml"
)
diff --git a/test/src/test-c-machine.cpp b/test/src/test-c-machine.cpp
index a9e5ecb..4bbc653 100644
--- a/test/src/test-c-machine.cpp
+++ b/test/src/test-c-machine.cpp
@@ -6,13 +6,22 @@
#include <deque> // deque
#include <boost/algorithm/string.hpp> // trim
-#define SCXML_VERBOSE 1
+//#define SCXML_VERBOSE
+
+#include "uscxml/config.h"
+
+#ifdef APPLE
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <pthread.h>
+#endif
#ifndef AUTOINCLUDE_TEST
#include "test-c-machine.machine.c"
#endif
#include "uscxml/Convenience.h"
+#include "uscxml/concurrency/Timer.h"
//#include "uscxml/DOMUtils.h"
#include "uscxml/Factory.h"
#include "uscxml/InterpreterInfo.h"
@@ -21,6 +30,10 @@
#include "uscxml/concurrency/DelayedEventQueue.h"
#include "uscxml/concurrency/tinythread.h"
+#ifdef BUILD_PROFILING
+# include "uscxml/plugins/DataModel.h"
+# endif
+
#define USER_DATA(ctx) ((GenCInterpreterInfo*)(((scxml_ctx*)ctx)->user_data))
using namespace uscxml;
@@ -82,33 +95,49 @@ public:
int matches(const char* desc, const char* event) {
const char* dPtr = desc;
const char* ePtr = event;
- while(true) {
+ while(*dPtr != 0) {
- // next event descriptor
- if (*dPtr == ' ') {
+ if (*dPtr == '*' && *ePtr != 0) // something following
+ return true;
+
+ // descriptor differs from event name
+ if (*dPtr != *ePtr) {
+ // move to next descriptor
+ while(*dPtr != ' ' && *dPtr != 0) {
+ dPtr++;
+ }
+ if (*dPtr == 0)
+ return false;
dPtr++;
ePtr = event;
- continue;
+ } else {
+ // move both pointers one character
+ dPtr++;
+ ePtr++;
+
}
// descriptor is done, return match
- if (*dPtr == 0 || *dPtr == '*')
+ if (((*dPtr == 0 || *dPtr == ' ') && (*ePtr == 0 || *ePtr == ' ')) || // exact match, end of string
+ (*dPtr == ' ' && *ePtr == '.') || (*dPtr == 0 && *ePtr == '.')) // prefix match
return true;
-
- // descriptor differs from event name
- if (*dPtr != *ePtr)
- return false;
-
- // move both pointers one character
- dPtr++;
- ePtr++;
}
+ return false;
}
int exec_content_raise(const scxml_ctx* ctx, const char* event) {
Event* e = new Event();
e->name = strdup(event);
+
+ if (boost::starts_with(e->name, "error.")) {
+ e->eventType = Event::PLATFORM;
+ } else {
+ e->eventType = Event::INTERNAL;
+ }
+
+#ifdef SCXML_VERBOSE
printf("Raising Internal Event: %s\n", e->name.c_str());
+#endif
USER_DATA(ctx)->iq.push_back(e);
return SCXML_ERR_OK;
}
@@ -145,9 +174,42 @@ int is_enabled(const scxml_ctx* ctx, const scxml_transition* t, const void* e) {
return false;
}
-int raise_done_event(const scxml_ctx* ctx, const scxml_state* state) {
- std::string doneName = "done.state.";
- exec_content_raise(ctx, (doneName + state->name).c_str());
+int raise_done_event(const scxml_ctx* ctx, const scxml_state* state, const scxml_elem_donedata* donedata) {
+ Event* e = new Event();
+ e->name = std::string("done.state.") + state->name;
+
+ if (donedata) {
+ if (donedata->content != NULL) {
+ e->data = Data(donedata->content, Data::VERBATIM);
+ } else if (donedata->contentexpr != NULL) {
+ try {
+ e->data = USER_DATA(ctx)->datamodel->getStringAsData(donedata->contentexpr);
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ }
+ } else {
+ try {
+ const scxml_elem_param* param = donedata->params;
+ while (param && ELEM_PARAM_IS_SET(param)) {
+ Data paramValue;
+ if (param->expr != NULL) {
+ paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->expr);
+ } else if(param->location) {
+ paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->location);
+ }
+ e->params.insert(std::make_pair(param->name, paramValue));
+ param++;
+ }
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ }
+ }
+ }
+
+#ifdef SCXML_VERBOSE
+ printf("Raising Done Event: %s\n", e->name.c_str());
+#endif
+ USER_DATA(ctx)->iq.push_back(e);
return SCXML_ERR_OK;
}
@@ -156,10 +218,16 @@ void delayedSend(void* ctx, std::string eventName) {
SendRequest* e = USER_DATA(ctx)->sendIds[eventName];
if (e->target == "#_internal") {
+ e->eventType = Event::INTERNAL;
+#ifdef SCXML_VERBOSE
printf("Pushing Internal Event: %s\n", e->name.c_str());
+#endif
USER_DATA(ctx)->iq.push_back(e);
} else {
+ e->eventType = Event::EXTERNAL;
+#ifdef SCXML_VERBOSE
printf("Pushing External Event: %s\n", e->name.c_str());
+#endif
USER_DATA(ctx)->eq.push_back(e);
}
USER_DATA(ctx)->monitor.notify_all();
@@ -182,6 +250,27 @@ int exec_content_cancel(const scxml_ctx* ctx, const char* sendid, const char* se
return SCXML_ERR_OK;
}
+std::string spaceNormalize(const std::string& text) {
+ std::stringstream content;
+ std::string seperator;
+
+ size_t start = 0;
+ for (int i = 0; i < text.size(); i++) {
+ if (isspace(text[i])) {
+ if (i > 0 && start < i) {
+ content << seperator << text.substr(start, i - start);
+ seperator = " ";
+ }
+ while(isspace(text[++i])); // skip whitespaces
+ start = i;
+ } else if (i + 1 == text.size()) {
+ content << seperator << text.substr(start, i + 1 - start);
+ }
+ }
+ return content.str();
+}
+
+
int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
SendRequest* e = new SendRequest();
@@ -190,6 +279,8 @@ int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
e->target = send->target;
} else if (send->targetexpr != NULL) {
e->target = USER_DATA(ctx)->datamodel->evalAsString(send->targetexpr);
+ } else {
+ e->target = "#_external";
}
if (e->target.size() > 0 && (e->target[0] != '#' || e->target[1] != '_')) {
@@ -198,12 +289,20 @@ int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
return SCXML_ERR_INVALID_TARGET;
}
- if (send->type != NULL) {
- e->type = send->type;
- } else if (send->typeexpr != NULL) {
- e->type = USER_DATA(ctx)->datamodel->evalAsString(send->typeexpr);
- } else {
- e->type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ e->origintype = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ e->origin = e->target;
+
+ try {
+ if (send->type != NULL) {
+ e->type = send->type;
+ } else if (send->typeexpr != NULL) {
+ e->type = USER_DATA(ctx)->datamodel->evalAsString(send->typeexpr);
+ } else {
+ e->type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ }
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
}
// only one somewhat supported
@@ -221,31 +320,67 @@ int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
e->name = strdup(send->event);
}
- const scxml_elem_param* param = send->params;
- while (param && ELEM_PARAM_IS_SET(param)) {
- Data paramValue;
- if (param->expr != NULL) {
- paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->expr);
- } else if(param->location) {
- paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->location);
+ try {
+ const scxml_elem_param* param = send->params;
+ while (param && ELEM_PARAM_IS_SET(param)) {
+ Data paramValue;
+ if (param->expr != NULL) {
+ paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->expr);
+ } else if(param->location) {
+ paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->location);
+ }
+ e->params.insert(std::make_pair(param->name, paramValue));
+ param++;
}
- e->params.insert(std::make_pair(param->name, paramValue));
- param++;
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
}
-
+
+ try {
+ if (send->namelist != NULL) {
+ const char* bPtr = &send->namelist[0];
+ const char* ePtr = bPtr;
+ while(*ePtr != '\0') {
+ ePtr++;
+ if (*ePtr == ' ' || *ePtr == '\0') {
+ std::string key(bPtr, ePtr - bPtr);
+ e->params.insert(std::make_pair(key, USER_DATA(ctx)->datamodel->getStringAsData(key)));
+ if (*ePtr == '\0')
+ break;
+ bPtr = ++ePtr;
+ }
+ }
+ }
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+
if (send->content != NULL) {
- e->data = Data(send->content, Data::VERBATIM);
+ // will it parse as json?
+ Data d = Data::fromJSON(send->content);
+ if (!d.empty()) {
+ e->data = d;
+ } else {
+ e->data = Data(spaceNormalize(send->content), Data::VERBATIM);
+ }
}
const char* sendid = NULL;
if (send->id != NULL) {
sendid = send->id;
+ e->sendid = sendid;
} else {
sendid = strdup(UUID::getUUID().c_str());
- if (send->idlocation != NULL)
+ if (send->idlocation != NULL) {
USER_DATA(ctx)->datamodel->assign(send->idlocation, Data(sendid, Data::VERBATIM));
+ } else {
+ e->hideSendId = true;
+ }
}
+
size_t delayMs = 0;
std::string delay;
if (send->delayexpr != NULL) {
@@ -299,9 +434,15 @@ int exec_content_init(const scxml_ctx* ctx, const scxml_elem_data* data) {
}
int exec_content_assign(const scxml_ctx* ctx, const char* location, const char* expr) {
+ std::string key = location;
+ if (key == "_sessionid" || key == "_name" || key == "_ioprocessors" || key == "_invokers" || key == "_event") {
+ exec_content_raise(ctx, "error.execution");
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+
try {
Data d(expr, Data::INTERPRETED);
- USER_DATA(ctx)->datamodel->assign(location, d);
+ USER_DATA(ctx)->datamodel->assign(key, d);
} catch (Event e) {
exec_content_raise(ctx, e.name.c_str());
return SCXML_ERR_EXEC_CONTENT;
@@ -350,10 +491,25 @@ int exec_content_foreach_done(const scxml_ctx* ctx, const scxml_elem_foreach* fo
}
int exec_content_log(const scxml_ctx* ctx, const char* label, const char* expr) {
- if (label != 0) {
- printf("%s: %s\n", label, expr);
- } else {
- printf("%s\n", USER_DATA(ctx)->datamodel->evalAsString(expr).c_str());
+ try {
+ if (label != 0) {
+// printf("%s: %s\n", label, USER_DATA(ctx)->datamodel->evalAsString(expr).c_str());
+ } else {
+// printf("%s\n", USER_DATA(ctx)->datamodel->evalAsString(expr).c_str());
+ }
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+
+ return SCXML_ERR_OK;
+}
+
+int exec_content_script(const scxml_ctx* ctx, const char* src, const char* content) {
+ if (content != NULL) {
+ USER_DATA(ctx)->datamodel->eval(Arabica::DOM::Element<std::string>(), content);
+ } else if (src != NULL) {
+ return SCXML_ERR_UNSUPPORTED;
}
return SCXML_ERR_OK;
}
@@ -366,7 +522,9 @@ void* dequeue_external(const scxml_ctx* ctx) {
Event* e = USER_DATA(ctx)->eq.front();
USER_DATA(ctx)->eq.pop_front();
USER_DATA(ctx)->datamodel->setEvent(*e);
+#ifdef SCXML_VERBOSE
printf("Popping External Event: %s\n", e->name.c_str());
+#endif
return e;
}
@@ -376,54 +534,125 @@ void* dequeue_internal(const scxml_ctx* ctx) {
Event* e = USER_DATA(ctx)->iq.front();
USER_DATA(ctx)->iq.pop_front();
USER_DATA(ctx)->datamodel->setEvent(*e);
+#ifdef SCXML_VERBOSE
printf("Popping Internal Event: %s\n", e->name.c_str());
+#endif
return e;
}
int main(int argc, char** argv) {
+
+#ifdef APPLE
+ mach_timebase_info_data_t timebase_info;
+ mach_timebase_info(&timebase_info);
+
+ const uint64_t NANOS_PER_MSEC = 1000000ULL;
+ double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
+
+ thread_time_constraint_policy_data_t policy;
+ policy.period = 0;
+ policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work
+ policy.constraint = (uint32_t)(10 * clock2abs);
+ policy.preemptible = FALSE;
+
+ int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
+ THREAD_TIME_CONSTRAINT_POLICY,
+ (thread_policy_t)&policy,
+ THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+ if (kr != KERN_SUCCESS) {
+ mach_error("thread_policy_set:", kr);
+ exit(1);
+ }
+#endif
+
int err;
+ size_t benchmarkRuns = 1;
+ const char* envBenchmarkRuns = getenv("USCXML_BENCHMARK_ITERATIONS");
+ if (envBenchmarkRuns != NULL) {
+ benchmarkRuns = strTo<size_t>(envBenchmarkRuns);
+ }
+
+ size_t remainingRuns = benchmarkRuns;
+
// setup info object required for datamodel
GenCInterpreterInfo interpreterInfo;
- interpreterInfo.name = "adsf";
+ interpreterInfo.name = SCXML_MACHINE_NAME;
interpreterInfo.sessionId = "rfwef";
- interpreterInfo.datamodel = Factory::getInstance()->createDataModel("ecmascript", &interpreterInfo);
interpreterInfo.delayQueue.start();
scxml_ctx ctx;
- memset(&ctx, 0, sizeof(scxml_ctx));
interpreterInfo.ctx = &ctx;
-
- // set info object as user data
- ctx.user_data = (void*)&interpreterInfo;
-
- // register callbacks with scxml context
- ctx.is_enabled = &is_enabled;
- ctx.is_true = &is_true;
- ctx.raise_done_event = &raise_done_event;
-
- ctx.exec_content_send = &exec_content_send;
- ctx.exec_content_raise = &exec_content_raise;
- ctx.exec_content_cancel = &exec_content_cancel;
- ctx.exec_content_log = &exec_content_log;
- ctx.exec_content_assign = &exec_content_assign;
- ctx.exec_content_foreach_init = &exec_content_foreach_init;
- ctx.exec_content_foreach_next = &exec_content_foreach_next;
- ctx.exec_content_foreach_done = &exec_content_foreach_done;
- ctx.dequeue_external = &dequeue_external;
- ctx.dequeue_internal = &dequeue_internal;
- ctx.exec_content_init = &exec_content_init;
-
- while((err = scxml_step(&ctx)) == SCXML_ERR_OK);
- assert(ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL);
-
- size_t passIdx = 0;
- for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
- if (scxml_states[i].name && strcmp(scxml_states[i].name, "pass") == 0) {
- passIdx = i;
- break;
+
+ double avg = 0;
+ size_t microSteps = 0;
+#ifdef BUILD_PROFILING
+ double avgDm = 0;
+#endif
+
+ while(remainingRuns-- > 0) {
+ memset(&ctx, 0, sizeof(scxml_ctx));
+
+ // fresh dm (expensive :( )
+ interpreterInfo.datamodel = Factory::getInstance()->createDataModel("ecmascript", &interpreterInfo);
+
+ // set info object as user data
+ ctx.user_data = (void*)&interpreterInfo;
+
+ // register callbacks with scxml context
+ ctx.is_enabled = &is_enabled;
+ ctx.is_true = &is_true;
+ ctx.raise_done_event = &raise_done_event;
+
+ ctx.exec_content_send = &exec_content_send;
+ ctx.exec_content_raise = &exec_content_raise;
+ ctx.exec_content_cancel = &exec_content_cancel;
+ ctx.exec_content_log = &exec_content_log;
+ ctx.exec_content_assign = &exec_content_assign;
+ ctx.exec_content_foreach_init = &exec_content_foreach_init;
+ ctx.exec_content_foreach_next = &exec_content_foreach_next;
+ ctx.exec_content_foreach_done = &exec_content_foreach_done;
+ ctx.dequeue_external = &dequeue_external;
+ ctx.dequeue_internal = &dequeue_internal;
+ ctx.exec_content_init = &exec_content_init;
+ ctx.exec_content_script = &exec_content_script;
+
+ Timer t;
+ t.start();
+
+ microSteps = 0;
+ while((err = scxml_step(&ctx)) == SCXML_ERR_OK) { microSteps++; }
+ assert(ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL);
+
+ t.stop();
+ avg += t.elapsed;
+#ifdef BUILD_PROFILING
+ avgDm += interpreterInfo.datamodel.get()->timer.elapsed;
+ interpreterInfo.datamodel.get()->timer.elapsed = 0;
+#endif
+ size_t passIdx = 0;
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (scxml_states[i].name && strcmp(scxml_states[i].name, "pass") == 0) {
+ passIdx = i;
+ break;
+ }
}
+
+ assert(IS_SET(passIdx, ctx.config));
+ interpreterInfo.delayQueue.cancelAllEvents();
+ interpreterInfo.eq.clear();
+ interpreterInfo.iq.clear();
}
- assert(IS_SET(passIdx, ctx.config));
+
+ // 14.25311111 us per microstep
+ // 1.923466667 us
+ std::cout << (avg * 1000.0) / (double)benchmarkRuns << " ms on average" << std::endl;
+ std::cout << microSteps << " microsteps per iteration (" << (avg * 1000.0) / ((double)benchmarkRuns * (double)microSteps) << " ms per microstep)" << std::endl;
+#ifdef BUILD_PROFILING
+ std::cout << (avgDm * 1000.0) / (double)benchmarkRuns << " ms in datamodel" << std::endl;
+ std::cout << ((avg - avgDm) * 1000.0) / ((double)benchmarkRuns * (double)microSteps) << " ms per microstep \\wo datamodel" << std::endl;
+#endif
+ interpreterInfo.delayQueue.stop();
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(100));
return EXIT_SUCCESS;
} \ No newline at end of file
diff --git a/test/src/test-c-machine.machine.c b/test/src/test-c-machine.machine.c
index d4c924d..3663eac 100644
--- a/test/src/test-c-machine.machine.c
+++ b/test/src/test-c-machine.machine.c
@@ -5,6 +5,9 @@
#define SET_BIT(idx, bitset) bitset[idx >> 3] |= (1 << (idx & 7));
#define CLEARBIT(idx, bitset) bitset[idx >> 3] &= (1 << (idx & 7)) ^ 0xFF;
+#define likely(x) (__builtin_expect (!!(x), 1))
+#define unlikely(x) (__builtin_expect (!!(x), 0))
+
// error return codes
#define SCXML_ERR_OK 0
#define SCXML_ERR_IDLE 1
@@ -14,13 +17,16 @@
#define SCXML_ERR_EXEC_CONTENT 5
#define SCXML_ERR_INVALID_TARGET 6
#define SCXML_ERR_INVALID_TYPE 7
+#define SCXML_ERR_UNSUPPORTED 8
-#define SCXML_NUMBER_STATES 5
-#define SCXML_NUMBER_TRANSITIONS 4
+#define SCXML_MACHINE_NAME ""
+#define SCXML_NUMBER_STATES 14
+#define SCXML_NUMBER_TRANSITIONS 10
#define SCXML_TRANS_SPONTANEOUS 0x01
#define SCXML_TRANS_TARGETLESS 0x02
#define SCXML_TRANS_INTERNAL 0x04
+#define SCXML_TRANS_HISTORY 0x08
#define SCXML_STATE_ATOMIC 0x01
#define SCXML_STATE_PARALLEL 0x02
@@ -37,6 +43,7 @@
#define SCXML_CTX_TRANSITION_FOUND 0x08
#define ELEM_DATA_IS_SET(data) (data->id != NULL)
+#define ELEM_DONEDATA_IS_SET(donedata) (donedata->content != NULL || donedata->contentexpr != NULL || donedata->params != NULL)
#define ELEM_PARAM_IS_SET(param) (param->name != NULL)
@@ -48,6 +55,7 @@ typedef struct scxml_invoke scxml_invoke;
typedef struct scxml_elem_send scxml_elem_send;
typedef struct scxml_elem_param scxml_elem_param;
typedef struct scxml_elem_data scxml_elem_data;
+typedef struct scxml_elem_donedata scxml_elem_donedata;
typedef struct scxml_elem_foreach scxml_elem_foreach;
typedef void* (*dequeue_internal_cb_t)(const scxml_ctx* ctx);
@@ -55,7 +63,7 @@ typedef void* (*dequeue_external_cb_t)(const scxml_ctx* ctx);
typedef int (*is_enabled_cb_t)(const scxml_ctx* ctx, const scxml_transition* transition, const void* event);
typedef int (*is_true_cb_t)(const scxml_ctx* ctx, const char* expr);
typedef int (*exec_content_t)(const scxml_ctx* ctx, const scxml_state* state, const void* event);
-typedef int (*raise_done_event_t)(const scxml_ctx* ctx, const scxml_state* state);
+typedef int (*raise_done_event_t)(const scxml_ctx* ctx, const scxml_state* state, const scxml_elem_donedata* donedata);
typedef int (*invoke_t)(const scxml_ctx* ctx, const scxml_state* s, const scxml_invoke* x);
typedef int (*exec_content_log_t)(const scxml_ctx* ctx, const char* label, const char* expr);
@@ -79,25 +87,26 @@ struct scxml_elem_data {
struct scxml_state {
const char* name; // eventual name
+ uint16_t source; // parent
exec_content_t on_entry; // on entry handlers
exec_content_t on_exit; // on exit handlers
invoke_t invoke; // invocations
- char children[1]; // all children
- char completion[1]; // default completion
- char ancestors[1]; // all ancestors
+ char children[2]; // all children
+ char completion[2]; // default completion
+ char ancestors[2]; // all ancestors
const scxml_elem_data* data;
uint8_t type; // atomic, parallel, compound, final, history
};
struct scxml_transition {
uint16_t source;
- char target[1];
+ char target[2];
const char* event;
const char* condition;
exec_content_t on_transition;
uint8_t type;
- char conflicts[1];
- char exit_set[1];
+ char conflicts[2];
+ char exit_set[2];
};
struct scxml_elem_foreach {
@@ -112,6 +121,13 @@ struct scxml_elem_param {
const char* location;
};
+struct scxml_elem_donedata {
+ uint16_t source;
+ const char* content;
+ const char* contentexpr;
+ const scxml_elem_param* params;
+};
+
struct scxml_elem_invoke {
const char* type;
const char* typeexpr;
@@ -124,6 +140,7 @@ struct scxml_elem_invoke {
const scxml_elem_param* params;
const exec_content_finalize_t* finalize;
const char* content;
+ const char* contentexpr;
void* user_data;
};
@@ -140,6 +157,7 @@ struct scxml_elem_send {
const char* delayexpr;
const char* namelist;
const char* content;
+ const char* contentexpr;
const scxml_elem_param* params;
void* user_data;
};
@@ -147,10 +165,10 @@ struct scxml_elem_send {
struct scxml_ctx {
uint8_t flags;
- char config[1];
- char history[1];
- char pending_invokes[1];
- char initialized_data[1];
+ char config[2];
+ char history[2];
+ char pending_invokes[2];
+ char initialized_data[2];
void* user_data;
@@ -173,75 +191,162 @@ struct scxml_ctx {
invoke_t invoke;
};
-scxml_elem_data scxml_elem_datas[4] = {
- { "Var1", NULL, NULL, NULL },
- { NULL, NULL, NULL, NULL },
- { "Var2", NULL, "1", NULL },
+static scxml_elem_data scxml_elem_datas[2] = {
+ { "Var1", NULL, "0", NULL },
{ NULL, NULL, NULL, NULL }
};
-int s1_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+static scxml_elem_send scxml_elem_sends[1] = {
+ { "timeout", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2s", NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+static int s0_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if likely(ctx->exec_content_assign != NULL) {
+ if ((ctx->exec_content_assign(ctx, "Var1", "Var1 + 1")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+static int s0_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ s0_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+static int s011_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if likely(ctx->exec_content_raise != NULL) {
+ if ((ctx->exec_content_raise(ctx, "entering.s011")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+static int s011_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ s011_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+static int s012_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if likely(ctx->exec_content_raise != NULL) {
+ if ((ctx->exec_content_raise(ctx, "entering.s012")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+static int s012_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ s012_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+static int s021_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
int err = SCXML_ERR_OK;
- if (ctx->exec_content_assign != NULL) {
- if ((ctx->exec_content_assign(ctx, "Var1", "Var2")) != SCXML_ERR_OK) return err;
+ if likely(ctx->exec_content_raise != NULL) {
+ if ((ctx->exec_content_raise(ctx, "entering.s021")) != SCXML_ERR_OK) return err;
} else {
return SCXML_ERR_MISSING_CALLBACK;
}
return SCXML_ERR_OK;
}
-int s1_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
- s1_on_entry_0(ctx, state, event);
+static int s021_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ s021_on_entry_0(ctx, state, event);
return SCXML_ERR_OK;
}
-int pass_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+static int s022_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
int err = SCXML_ERR_OK;
- if (ctx->exec_content_log != NULL) {
- if ((ctx->exec_content_log(ctx, "Outcome", "'pass'")) != SCXML_ERR_OK) return err;
+ if likely(ctx->exec_content_raise != NULL) {
+ if ((ctx->exec_content_raise(ctx, "entering.s022")) != SCXML_ERR_OK) return err;
} else {
return SCXML_ERR_MISSING_CALLBACK;
}
return SCXML_ERR_OK;
}
-int pass_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+static int s022_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ s022_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+static int pass_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if likely(ctx->exec_content_log != NULL) {
+ if unlikely((ctx->exec_content_log(ctx, "Outcome", "'pass'")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+static int pass_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
pass_on_entry_0(ctx, state, event);
return SCXML_ERR_OK;
}
-int fail_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+static int fail_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
int err = SCXML_ERR_OK;
- if (ctx->exec_content_log != NULL) {
- if ((ctx->exec_content_log(ctx, "Outcome", "'fail'")) != SCXML_ERR_OK) return err;
+ if likely(ctx->exec_content_log != NULL) {
+ if unlikely((ctx->exec_content_log(ctx, "Outcome", "'fail'")) != SCXML_ERR_OK) return err;
} else {
return SCXML_ERR_MISSING_CALLBACK;
}
return SCXML_ERR_OK;
}
-int fail_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+static int fail_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
fail_on_entry_0(ctx, state, event);
return SCXML_ERR_OK;
}
-scxml_state scxml_states[5] = {
- { NULL, NULL, NULL, NULL, { 0x1e /* 01111, 1 2 3 4 */ }, { 0x02 /* 01000, 1 */ }, { 0x00 /* 00000, */ }, (const scxml_elem_data*)&scxml_elem_datas[0], SCXML_STATE_COMPOUND },
- { "s0", NULL, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_ATOMIC },
- { "s1", s1_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, (const scxml_elem_data*)&scxml_elem_datas[1], SCXML_STATE_ATOMIC },
- { "pass", pass_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_FINAL },
- { "fail", fail_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_FINAL }
+static int s0_transition0_on_trans(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if likely(ctx->exec_content_send != NULL) {
+ if ((ctx->exec_content_send(ctx, &scxml_elem_sends[0])) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+static scxml_state scxml_states[14] = {
+ { NULL, 0, NULL, NULL, NULL, { 0x02, 0x3c /* 01000000001111, 1 10 11 12 13 */ }, { 0x40, 0x00 /* 00000010000000, 6 */ }, { 0x00, 0x00 /* 00000000000000, */ }, (const scxml_elem_data*)&scxml_elem_datas[0], SCXML_STATE_COMPOUND },
+ { "s0", 0, s0_on_entry, NULL, NULL, { 0x9c, 0x00 /* 00111001000000, 2 3 4 7 */ }, { 0x10, 0x00 /* 00001000000000, 4 */ }, { 0x01, 0x00 /* 10000000000000, 0 */ }, NULL, SCXML_STATE_COMPOUND },
+ { "s0HistShallow", 1, NULL, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x90, 0x00 /* 00001001000000, 4 7 */ }, { 0x03, 0x00 /* 11000000000000, 0 1 */ }, NULL, SCXML_STATE_HISTORY_SHALLOW },
+ { "s0HistDeep", 1, NULL, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0xf0, 0x03 /* 00001111110000, 4 5 6 7 8 9 */ }, { 0x03, 0x00 /* 11000000000000, 0 1 */ }, NULL, SCXML_STATE_HISTORY_DEEP },
+ { "s01", 1, NULL, NULL, NULL, { 0x60, 0x00 /* 00000110000000, 5 6 */ }, { 0x20, 0x00 /* 00000100000000, 5 */ }, { 0x03, 0x00 /* 11000000000000, 0 1 */ }, NULL, SCXML_STATE_COMPOUND },
+ { "s011", 4, s011_on_entry, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x00, 0x00 /* 00000000000000, */ }, { 0x13, 0x00 /* 11001000000000, 0 1 4 */ }, NULL, SCXML_STATE_ATOMIC },
+ { "s012", 4, s012_on_entry, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x00, 0x00 /* 00000000000000, */ }, { 0x13, 0x00 /* 11001000000000, 0 1 4 */ }, NULL, SCXML_STATE_ATOMIC },
+ { "s02", 1, NULL, NULL, NULL, { 0x00, 0x03 /* 00000000110000, 8 9 */ }, { 0x00, 0x01 /* 00000000100000, 8 */ }, { 0x03, 0x00 /* 11000000000000, 0 1 */ }, NULL, SCXML_STATE_COMPOUND },
+ { "s021", 7, s021_on_entry, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x00, 0x00 /* 00000000000000, */ }, { 0x83, 0x00 /* 11000001000000, 0 1 7 */ }, NULL, SCXML_STATE_ATOMIC },
+ { "s022", 7, s022_on_entry, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x00, 0x00 /* 00000000000000, */ }, { 0x83, 0x00 /* 11000001000000, 0 1 7 */ }, NULL, SCXML_STATE_ATOMIC },
+ { "s1", 0, NULL, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x00, 0x00 /* 00000000000000, */ }, { 0x01, 0x00 /* 10000000000000, 0 */ }, NULL, SCXML_STATE_ATOMIC },
+ { "s2", 0, NULL, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x00, 0x00 /* 00000000000000, */ }, { 0x01, 0x00 /* 10000000000000, 0 */ }, NULL, SCXML_STATE_ATOMIC },
+ { "pass", 0, pass_on_entry, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x00, 0x00 /* 00000000000000, */ }, { 0x01, 0x00 /* 10000000000000, 0 */ }, NULL, SCXML_STATE_FINAL },
+ { "fail", 0, fail_on_entry, NULL, NULL, { 0x00, 0x00 /* 00000000000000, */ }, { 0x00, 0x00 /* 00000000000000, */ }, { 0x01, 0x00 /* 10000000000000, 0 */ }, NULL, SCXML_STATE_FINAL }
};
-scxml_transition scxml_transitions[4] = {
- { 1, { 0x04 /* 00100 */ }, NULL, "typeof Var2 === 'undefined' ", NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
- { 1, { 0x10 /* 00001 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
- { 2, { 0x08 /* 00010 */ }, NULL, "Var1===Var2", NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
- { 2, { 0x10 /* 00001 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } }
+static scxml_transition scxml_transitions[10] = {
+ { 2, { 0x80, 0x00 /* 00000001000000 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS | SCXML_TRANS_HISTORY, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 3, { 0x00, 0x02 /* 00000000010000 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS | SCXML_TRANS_HISTORY, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 1, { 0x00, 0x04 /* 00000000001000 */ }, "entering.s012", "Var1==1", s0_transition0_on_trans, 0, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 1, { 0x00, 0x08 /* 00000000000100 */ }, "entering.s012", "Var1==2", NULL, 0, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 1, { 0x00, 0x20 /* 00000000000001 */ }, "entering", "Var1==2", NULL, 0, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 1, { 0x00, 0x10 /* 00000000000010 */ }, "entering.s011", "Var1==3", NULL, 0, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 1, { 0x00, 0x20 /* 00000000000001 */ }, "entering", "Var1==3", NULL, 0, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 1, { 0x00, 0x20 /* 00000000000001 */ }, "timeout", NULL, NULL, 0, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 10, { 0x08, 0x00 /* 00010000000000 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } },
+ { 11, { 0x04, 0x00 /* 00100000000000 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS, { 0xff, 0x03 /* 1111111111 */ }, { 0xfe, 0x3f /* 01111111111111 */ } }
};
#ifdef SCXML_VERBOSE
-void printStateNames(const char* a) {
+static void printStateNames(const char* a) {
const char* seperator = "";
for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
if (IS_SET(i, a)) {
@@ -252,7 +357,7 @@ void printStateNames(const char* a) {
printf("\n");
}
-void printBitsetIndices(const char* a, size_t length) {
+static void printBitsetIndices(const char* a, size_t length) {
const char* seperator = "";
for (int i = 0; i < length; i++) {
if (IS_SET(i, a)) {
@@ -264,43 +369,50 @@ void printBitsetIndices(const char* a, size_t length) {
}
#endif
-void bit_or(char* dest, const char* mask, size_t length) {
- for (int i = 0; i < length; ++i) {
- dest[i] |= mask[i];
- }
+static void bit_or(char* dest, const char* mask, size_t i) {
+ do {
+ dest[i - 1] |= mask[i - 1];
+ } while(--i);
}
-void bit_copy(char* dest, const char* source, size_t length) {
- for (int i = 0; i < length; ++i) {
- dest[i] = source[i];
- }
+static void bit_copy(char* dest, const char* source, size_t i) {
+ do {
+ dest[i - 1] = source[i - 1];
+ } while(--i);
}
-int bit_has_and(const char* a, const char* b, size_t length) {
- for (int i = 0; i < length; ++i) {
- if (a[i] & b[i])
+static int bit_has_and(const char* a, const char* b, size_t i) {
+ do {
+ if (a[i - 1] & b[i - 1])
return true;
- }
+ } while(--i);
return false;
}
-void bit_and_not(char* dest, const char* mask, size_t length) {
- for (int i = 0; i < length; ++i) {
- dest[i] &= ~mask[i];
- }
+static void bit_and_not(char* dest, const char* mask, size_t i) {
+ do {
+ dest[i - 1] &= ~mask[i - 1];
+ } while(--i);
+}
+
+static void bit_and(char* dest, const char* mask, size_t i) {
+ do {
+ dest[i - 1] &= mask[i - 1];
+ } while(--i);
}
-int bit_any_set(const char* a, size_t length) {
- for (int i = 0; i < length; ++i) {
- if (a[i] > 0)
+static int bit_any_set(const char* a, size_t i) {
+ do {
+ if (a[i - 1] > 0)
return true;
- }
+ } while(--i);
return false;
}
-int scxml_step(scxml_ctx* ctx) {
+static int scxml_step(scxml_ctx* ctx) {
#ifdef SCXML_VERBOSE
+ printf("Config: ");
printStateNames(ctx->config);
#endif
@@ -311,15 +423,15 @@ MACRO_STEP:
return SCXML_ERR_DONE;
int err = SCXML_ERR_OK;
- char conflicts[1] = {0};
- char target_set[1] = {0};
- char exit_set[1] = {0};
- char trans_set[1] = {0};
- char entry_set[1] = {0};
+ char conflicts[2] = {0, 0};
+ char target_set[2] = {0, 0};
+ char exit_set[2] = {0, 0};
+ char trans_set[2] = {0, 0};
+ char entry_set[2] = {0, 0};
void* event;
- if (ctx->flags == SCXML_CTX_PRISTINE) {
- bit_or(target_set, scxml_states[0].completion, 1);
+ if unlikely(ctx->flags == SCXML_CTX_PRISTINE) {
+ bit_or(target_set, scxml_states[0].completion, 2);
ctx->flags |= SCXML_CTX_SPONTANEOUS | SCXML_CTX_INITIALIZED;
goto COMPLETE_CONFIG;
}
@@ -337,6 +449,10 @@ MACRO_STEP:
SELECT_TRANSITIONS:
for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {
+ // never select history or initial transitions automatically
+ if unlikely(scxml_transitions[i].type & (SCXML_TRANS_HISTORY | SCXML_TRANS_HISTORY))
+ continue;
+
// is the transition active?
if (IS_SET(scxml_transitions[i].source, ctx->config)) {
// is it non-conflicting?
@@ -347,46 +463,69 @@ SELECT_TRANSITIONS:
ctx->flags |= SCXML_CTX_TRANSITION_FOUND;
// transitions that are pre-empted
- bit_or(conflicts, scxml_transitions[i].conflicts, 1);
+ bit_or(conflicts, scxml_transitions[i].conflicts, 2);
// states that are directly targeted (resolve as entry-set later)
- bit_or(target_set, scxml_transitions[i].target, 1);
+ bit_or(target_set, scxml_transitions[i].target, 2);
// states that will be left
- bit_or(exit_set, scxml_transitions[i].exit_set, 1);
+ bit_or(exit_set, scxml_transitions[i].exit_set, 2);
SET_BIT(i, trans_set);
}
}
}
}
+ bit_and(exit_set, ctx->config, 2);
if (ctx->flags & SCXML_CTX_TRANSITION_FOUND) {
ctx->flags |= SCXML_CTX_SPONTANEOUS;
} else {
ctx->flags &= ~SCXML_CTX_SPONTANEOUS;
- goto MACRO_STEP;
+ // goto MACRO_STEP;
+ return SCXML_ERR_OK;
}
+#ifdef SCXML_VERBOSE
+ printf("Targets: ");
+ printStateNames(target_set);
+#endif
+
REMEMBER_HISTORY:
- // are my ancestors in the exit set?
for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
- if (IS_SET(i, ctx->config) && bit_has_and(exit_set, scxml_states[i].ancestors, 1)) {
- SET_BIT(i, ctx->history);
+ if unlikely(scxml_states[i].type == SCXML_STATE_HISTORY_SHALLOW || scxml_states[i].type == SCXML_STATE_HISTORY_DEEP) {
+ // a history state whose parent is about to be exited
+ if unlikely(IS_SET(scxml_states[i].source, exit_set)) {
+ char history[2] = {0, 0};
+ bit_copy(history, scxml_states[i].completion, 2);
+
+ // set those states who were enabled
+ bit_and(history, ctx->config, 2);
+
+ // clear current history with completion mask
+ bit_and_not(ctx->history, scxml_states[i].completion, 2);
+
+ // set history
+ bit_or(ctx->history, history, 2);
+ }
}
}
-
#ifdef SCXML_VERBOSE
printf("Exiting: ");
printStateNames(exit_set);
#endif
+#ifdef SCXML_VERBOSE
+ printf("History: ");
+ printStateNames(ctx->history);
+#endif
+
EXIT_STATES:
for (int i = SCXML_NUMBER_STATES - 1; i >= 0; i--) {
if (IS_SET(i, exit_set) && IS_SET(i, ctx->config)) {
// call all on exit handlers
if (scxml_states[i].on_exit != NULL) {
- if((err = scxml_states[i].on_exit(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)
+ if unlikely((err = scxml_states[i].on_exit(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)
return err;
}
CLEARBIT(i, ctx->config);
@@ -395,12 +534,12 @@ EXIT_STATES:
COMPLETE_CONFIG:
// calculate new entry set
- bit_copy(entry_set, target_set, 1);
+ bit_copy(entry_set, target_set, 2);
// iterate for ancestors
for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
if (IS_SET(i, entry_set)) {
- bit_or(entry_set, scxml_states[i].ancestors, 1);
+ bit_or(entry_set, scxml_states[i].ancestors, 2);
}
}
@@ -410,7 +549,27 @@ ADD_DESCENDANTS:
if (IS_SET(i, entry_set)) {
switch (scxml_states[i].type) {
case SCXML_STATE_PARALLEL: {
- bit_or(entry_set, scxml_states[i].completion, 1);
+ bit_or(entry_set, scxml_states[i].completion, 2);
+ break;
+ }
+ case SCXML_STATE_HISTORY_SHALLOW:
+ case SCXML_STATE_HISTORY_DEEP: {
+ char history_targets[2] = {0, 0};
+ if (!bit_has_and(scxml_states[i].completion, ctx->history, 2)) {
+ // nothing set for history, look for a default transition or enter parents completion
+ for (int j = 0; j < SCXML_NUMBER_TRANSITIONS; j++) {
+ if unlikely(scxml_transitions[j].source == i) {
+ bit_or(entry_set, scxml_transitions[j].target, 2);
+ SET_BIT(j, trans_set);
+ break;
+ }
+ }
+ // TODO: enter parents default completion here
+ } else {
+ bit_copy(history_targets, scxml_states[i].completion, 2);
+ bit_and(history_targets, ctx->history, 2);
+ bit_or(entry_set, history_targets, 2);
+ }
break;
}
case SCXML_STATE_INITIAL: {
@@ -418,16 +577,18 @@ ADD_DESCENDANTS:
if (scxml_transitions[j].source == i) {
SET_BIT(j, trans_set);
CLEARBIT(i, entry_set);
- bit_or(entry_set, scxml_transitions[j].target, 1);
+ bit_or(entry_set, scxml_transitions[j].target, 2);
// one target may have been above, reestablish completion
- goto ADD_DESCENDANTS;
+ // goto ADD_DESCENDANTS; // initial will have to be first!
}
}
break;
}
case SCXML_STATE_COMPOUND: { // we need to check whether one child is already in entry_set
- if (!bit_has_and(entry_set, scxml_states[i].children, 1)) {
- bit_or(entry_set, scxml_states[i].completion, 1);
+ if (!bit_has_and(entry_set, scxml_states[i].children, 2) &&
+ !bit_has_and(ctx->config, scxml_states[i].children, 2))
+ {
+ bit_or(entry_set, scxml_states[i].completion, 2);
}
break;
}
@@ -437,15 +598,15 @@ ADD_DESCENDANTS:
#ifdef SCXML_VERBOSE
printf("Transitions: ");
- printBitsetIndices(trans_set, sizeof(char) * 8 * 1);
+ printBitsetIndices(trans_set, sizeof(char) * 8 * 2);
#endif
TAKE_TRANSITIONS:
for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {
- if (IS_SET(i, trans_set)) {
+ if (IS_SET(i, trans_set) && (scxml_transitions[i].type & SCXML_TRANS_HISTORY) == 0) {
// call executable content in transition
if (scxml_transitions[i].on_transition != NULL) {
- if((err = scxml_transitions[i].on_transition(ctx,
+ if unlikely((err = scxml_transitions[i].on_transition(ctx,
&scxml_states[scxml_transitions[i].source],
event)) != SCXML_ERR_OK)
return err;
@@ -461,39 +622,43 @@ TAKE_TRANSITIONS:
ENTER_STATES:
for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
if (IS_SET(i, entry_set) && !IS_SET(i, ctx->config)) {
+ // these are no proper states
+ if unlikely(scxml_states[i].type == SCXML_STATE_HISTORY_DEEP ||
+ scxml_states[i].type == SCXML_STATE_HISTORY_SHALLOW ||
+ scxml_states[i].type == SCXML_STATE_INITIAL)
+ continue;
+
SET_BIT(i, ctx->config);
- if (scxml_states[i].on_entry != NULL) {
- if((err = scxml_states[i].on_entry(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)
- return err;
- }
// initialize data
- if(!IS_SET(i, ctx->initialized_data)) {
- if (scxml_states[i].data != NULL && ctx->exec_content_init != NULL) {
+ if (!IS_SET(i, ctx->initialized_data)) {
+ if unlikely(scxml_states[i].data != NULL && ctx->exec_content_init != NULL) {
ctx->exec_content_init(ctx, scxml_states[i].data);
}
SET_BIT(i, ctx->initialized_data);
}
+ if (scxml_states[i].on_entry != NULL) {
+ if unlikely((err = scxml_states[i].on_entry(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)
+ return err;
+ }
+
// handle final states
- if (scxml_states[i].type == SCXML_STATE_FINAL) {
- if (scxml_states[i].ancestors[0] == 0x01) {
+ if unlikely(scxml_states[i].type == SCXML_STATE_FINAL) {
+ if unlikely(scxml_states[i].ancestors[0] == 0x01) {
ctx->flags |= SCXML_CTX_TOP_LEVEL_FINAL;
} else {
// raise done event
size_t parent = 0;
- for (int j = 0; j < SCXML_NUMBER_STATES; j++) {
+ for (int j = SCXML_NUMBER_STATES - 1; j >= 0; j--) {
// we could trade runtime for memory here by saving the parent index
- if (!IS_SET(j, scxml_states[i].ancestors)) {
- if (parent != 0) {
- break;
- }
- continue;
- } else {
- parent = j;
+ if unlikely(IS_SET(j, scxml_states[i].ancestors)) {
+ parent = j;
+ break;
}
}
- ctx->raise_done_event(ctx, &scxml_states[parent]);
+ // is this raised for toplevel final as well?
+ ctx->raise_done_event(ctx, &scxml_states[parent], NULL);
}
/**
@@ -504,11 +669,11 @@ ENTER_STATES:
* 4. If a state remains, not all children of a parallel are final
*/
for (int j = 0; j < SCXML_NUMBER_STATES; j++) {
- if (scxml_states[j].type == SCXML_STATE_PARALLEL) {
+ if unlikely(scxml_states[j].type == SCXML_STATE_PARALLEL) {
char parallel_children[2] = {0, 0};
size_t parallel = j;
for (int k = 0; k < SCXML_NUMBER_STATES; k++) {
- if (IS_SET(parallel, scxml_states[k].ancestors) && IS_SET(k, ctx->config)) {
+ if unlikely(IS_SET(parallel, scxml_states[k].ancestors) && IS_SET(k, ctx->config)) {
if (scxml_states[k].type == SCXML_STATE_FINAL) {
bit_and_not(parallel_children, scxml_states[k].ancestors, 2);
} else {
@@ -516,8 +681,8 @@ ENTER_STATES:
}
}
}
- if (!bit_any_set(parallel_children, 2)) {
- ctx->raise_done_event(ctx, &scxml_states[parallel]);
+ if unlikely(!bit_any_set(parallel_children, 2)) {
+ ctx->raise_done_event(ctx, &scxml_states[parallel], NULL);
}
}
}
@@ -527,6 +692,19 @@ ENTER_STATES:
}
}
+HISTORY_TRANSITIONS:
+ for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {
+ if unlikely(IS_SET(i, trans_set) && (scxml_transitions[i].type & SCXML_TRANS_HISTORY)) {
+ // call executable content in transition
+ if (scxml_transitions[i].on_transition != NULL) {
+ if unlikely((err = scxml_transitions[i].on_transition(ctx,
+ &scxml_states[scxml_transitions[i].source],
+ event)) != SCXML_ERR_OK)
+ return err;
+ }
+ }
+ }
+
return SCXML_ERR_OK;
}
diff --git a/test/src/test-misc.cpp b/test/src/test-misc.cpp
index 09cda31..6259d57 100644
--- a/test/src/test-misc.cpp
+++ b/test/src/test-misc.cpp
@@ -1,3 +1,24 @@
+#include "uscxml/config.h"
+#include "uscxml/Common.h"
+#include "uscxml/concurrency/Timer.h"
+#include "uscxml/concurrency/tinythread.h"
+
+#include <iostream>
+
+using namespace uscxml;
+
+Timer t1;
+
+bool testTimers() {
+ {
+ Measurement m(&t1);
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(1000));
+ }
+ std::cout << t1.elapsed << std::endl;
+ return true;
+}
+
int main(int argc, char** argv) {
- return 0;
+ testTimers();
+ return 0;
} \ No newline at end of file
diff --git a/test/src/test-w3c.cpp b/test/src/test-w3c.cpp
index 0069d0d..a44fb57 100644
--- a/test/src/test-w3c.cpp
+++ b/test/src/test-w3c.cpp
@@ -1,4 +1,8 @@
+// I feel dirty, but we need to access the datamodel timer
+#define protected public
+
#include "uscxml/config.h"
+#include "uscxml/Convenience.h"
#include "uscxml/Interpreter.h"
#include "uscxml/DOMUtils.h"
@@ -13,6 +17,10 @@
#include <signal.h>
#endif
+#ifdef BUILD_PROFILING
+# include "uscxml/plugins/DataModel.h"
+# endif
+
#ifdef _WIN32
#include "XGetopt.h"
#include "XGetopt.cpp"
@@ -50,7 +58,7 @@ void printUsageAndExit(const char* progName) {
exit(1);
}
-class W3CStatusMonitor : public uscxml::StateTransitionMonitor {
+class W3CStatusMonitor : public uscxml::InterpreterMonitor {
void beforeCompletion(uscxml::Interpreter tmp) {
if (interpreter.getConfiguration().size() == 1 && interpreter.isInState("pass")) {
@@ -101,7 +109,12 @@ int main(int argc, char** argv) {
break;
}
}
-
+
+ const char* envBenchmarkRuns = getenv("USCXML_BENCHMARK_ITERATIONS");
+ if (envBenchmarkRuns != NULL) {
+ benchmarkRuns = strTo<size_t>(envBenchmarkRuns);
+ }
+
documentURI = argv[optind];
LOG(INFO) << "Processing " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor)) << (benchmarkRuns > 0 ? " for " + toStr(benchmarkRuns) + " benchmarks" : "");
@@ -145,34 +158,50 @@ int main(int argc, char** argv) {
}
if (interpreter) {
+ W3CStatusMonitor* vm = new W3CStatusMonitor();
+ interpreter.addMonitor(vm);
+
if (benchmarkRuns > 0) {
LOG(INFO) << "Benchmarking " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor));
InterpreterState state = interpreter.getState();
-
+
double avg = 0;
- uint64_t now = 0;
+#ifdef BUILD_PROFILING
+ double avgDm = 0;
+ double avgStep = 0;
+#endif
size_t remainingRuns = benchmarkRuns;
uint64_t start = tthread::chrono::system_clock::now();
while(remainingRuns-- > 0) {
- now = tthread::chrono::system_clock::now();
+ Timer t;
+ t.start();
for(;;) {
state = interpreter.step(true);
+ if (state == USCXML_FINISHED) {
+#ifdef BUILD_PROFILING
+ avgDm += interpreter.getDataModel()._impl.get()->timer.elapsed;
+ interpreter.getDataModel()._impl.get()->timer.elapsed = 0;
+ avgStep += interpreter.getImpl()->timer.elapsed;
+#endif
+ }
if (state < 0)
break;
}
- avg += (double)(tthread::chrono::system_clock::now() - now) / (double)benchmarkRuns;
+ t.stop();
+ avg += t.elapsed;
interpreter.reset();
}
uint64_t totalDuration = tthread::chrono::system_clock::now() - start;
std::cout << benchmarkRuns << " iterations in " << totalDuration << " ms" << std::endl;
- std::cout << avg << " ms on average" << std::endl;
-
- } else {
- W3CStatusMonitor* vm = new W3CStatusMonitor();
- interpreter.addMonitor(vm);
+ std::cout << (avg * 1000.0) / (double)benchmarkRuns << " ms on average" << std::endl;
+#ifdef BUILD_PROFILING
+ std::cout << (avgDm * 1000.0) / (double)benchmarkRuns << " ms in datamodel" << std::endl;
+ std::cout << (avgStep * 1000.0) / (double)benchmarkRuns << " ms in microsteps" << std::endl;
+#endif
+ } else {
interpreter.start();
while(interpreter.runOnMainThread(25));
}
diff --git a/test/w3c/run_generated_c_test.cmake b/test/w3c/run_generated_c_test.cmake
index 7ae7b4d..2c35762 100644
--- a/test/w3c/run_generated_c_test.cmake
+++ b/test/w3c/run_generated_c_test.cmake
@@ -10,8 +10,11 @@ if(CMD_RESULT)
endif()
message(STATUS "time for transforming to c machine")
+# message(FATAL_ERROR "PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}")
+
set(COMPILE_CMD
"-o" "${OUTDIR}/${TEST_FILE_NAME}"
+"-Ofast"
"-L${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
"-luscxml64"
"-include" "${OUTDIR}/${TEST_FILE_NAME}.machine.c"
@@ -19,6 +22,7 @@ set(COMPILE_CMD
"-I${PROJECT_SOURCE_DIR}/contrib/prebuilt/${USCXML_PLATFORM_ID}/include/arabica"
"-I${PROJECT_SOURCE_DIR}/contrib/prebuilt/include"
"-I${CMAKE_BINARY_DIR}"
+"-I${PROJECT_BINARY_DIR}"
"-I${PROJECT_SOURCE_DIR}/src"
"-Wl,-rpath,${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
"-DAUTOINCLUDE_TEST=ON"