From 9f4d810400550d1b98ab944cd96f937720eb6b0d Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Fri, 8 Jan 2016 23:15:37 +0100 Subject: Fixed compilation for gcc on Linux again --- CMakeLists.txt | 1 + config.h.in | 1 + src/uscxml/Interpreter.cpp | 17 +- src/uscxml/Interpreter.h | 9 + src/uscxml/concurrency/DelayedEventQueue.cpp | 10 + src/uscxml/concurrency/DelayedEventQueue.h | 3 +- src/uscxml/concurrency/Timer.cpp | 116 ++++++ src/uscxml/concurrency/Timer.h | 68 ++++ src/uscxml/plugins/DataModel.h | 9 + .../ecmascript/JavaScriptCore/JSCDataModel.cpp | 31 +- .../ecmascript/JavaScriptCore/JSCDataModel.h | 1 + src/uscxml/transform/ChartToC.cpp | 430 +++++++++++++++------ src/uscxml/transform/ChartToC.h | 3 + src/uscxml/transform/ChartToMinimalSCXML.h | 1 - test/CMakeLists.txt | 1 + test/ctest/CTestCustom.ctest.in | 37 ++ test/src/test-c-machine.cpp | 375 ++++++++++++++---- test/src/test-c-machine.machine.c | 400 +++++++++++++------ test/src/test-misc.cpp | 23 +- test/src/test-w3c.cpp | 51 ++- test/w3c/run_generated_c_test.cmake | 4 + 21 files changed, 1278 insertions(+), 313 deletions(-) create mode 100644 src/uscxml/concurrency/Timer.cpp create mode 100644 src/uscxml/concurrency/Timer.h 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 InterpreterImpl::getDocumentInitialTransitions() { } InterpreterState InterpreterImpl::step(int waitForMS) { - try { + TIME_BLOCK + try { tthread::lock_guard 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 // 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& 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 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 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 +#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 + + 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 + + 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 + + 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 + + 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 #include #include @@ -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 +#ifdef BUILD_PROFILING +#define TIME_BLOCK Measurement msm(&timer); +#else +#define TIME_BLOCK (0); +#endif + #ifdef BUILD_AS_PLUGINS #include #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 JSCDataModel::create(InterpreterInfo* interpreter) { boost::shared_ptr dm = boost::shared_ptr(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& scriptElem, const std::string& expr) { + TIME_BLOCK evalAsValue(expr); } bool JSCDataModel::evalAsBool(const Arabica::DOM::Element& 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& assignElem, const Node& 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& assignElem, } JSValueRef JSCDataModel::getNodeAsValue(const Node& node) { + TIME_BLOCK switch (node.getNodeType()) { case Node_base::ELEMENT_NODE: { TO_JSC_DOMVALUE(Element); @@ -689,6 +714,7 @@ JSValueRef JSCDataModel::getNodeAsValue(const Node& 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& dataElem, const Node& node, const std::string& content) { + TIME_BLOCK try { assign(dataElem, node, content); } catch (Event e) { @@ -713,6 +740,7 @@ void JSCDataModel::init(const Element& 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 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 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 state(_states[i]); + if (i == 0) { + // root state - we need to perform some initialization here + NodeSet 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 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 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 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 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 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(execContent[0]), 1); + for (int j = 0; j < execContent.size(); j++) { + writeExecContent(stream, Element(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::Nodeexec_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 scriptTexts = filterChildType(Node_base::TEXT_NODE, elem); @@ -524,16 +579,16 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Nodeexec_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::Nodeexec_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 child = node.getFirstChild(); while(child) { @@ -562,7 +617,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Nodeis_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 child = elem.getFirstChild(); @@ -585,7 +640,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Nodeexec_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::Nodeexec_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::Nodeexec_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::Nodeexec_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 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 foreach(foreachs[i]); stream << " { "; @@ -663,32 +718,37 @@ void ChartToC::writeElementInfo(std::ostream& stream) { stream << "};" << std::endl; stream << std::endl; } - + NodeSet datas = filterChildElements(_nsInfo.xmlNSPrefix + "data", _scxml, true); if (datas.size() > 0) { - if (_binding == InterpreterImpl::EARLY) { - Element(_states[0]).setAttribute("dataIndex", "0"); - } - + size_t dataIndexOffset = 0; Node parent; size_t distinctParents = 0; - for (int i = 0; i < datas.size(); i++) { - Element data(datas[i]); - if (data.getParentNode() != parent) { - distinctParents++; + + if (_binding == InterpreterImpl::EARLY) { + Element(_states[0]).setAttribute("dataIndex", "0"); + distinctParents = 1; + } else { + for (int i = 0; i < datas.size(); i++) { + Element data(datas[i]); + if (data.getParentNode() != parent) { + distinctParents++; + } } } + parent = Node(); - 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 data(datas[i]); if (data.getParentNode().getParentNode() != parent) { if (_binding == InterpreterImpl::LATE) { - Element(data.getParentNode().getParentNode()).setAttribute("dataIndex", toStr(i)); if (i > 0) { stream << " { NULL, NULL, NULL, NULL }," << std::endl; + dataIndexOffset++; } + Element(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(); - 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 param(params[i]); if (param.getParentNode() != parent) { @@ -751,7 +810,7 @@ void ChartToC::writeElementInfo(std::ostream& stream) { NodeSet 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 send(sends[i]); stream << " { "; @@ -770,15 +829,14 @@ void ChartToC::writeElementInfo(std::ostream& stream) { NodeSet contents = filterChildElements(_nsInfo.xmlNSPrefix + "content", send); if (contents.size() > 0) { std::stringstream ss; - stream << "\""; NodeList 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 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 donedata(donedatas[i]); + stream << " { "; + + // parent + stream << ATTR_CAST(donedata.getParentNode(), "documentOrder") << ", "; + + NodeSet contents = filterChildElements(_nsInfo.xmlNSPrefix + "content", donedata); + if (contents.size() > 0) { + std::stringstream ss; + NodeList 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 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 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(_states[j]))) { + completion.push_back(_states[j]); + } + } else { + if (_states[j].getParentNode() == state.getParentNode() && !isHistory(Element(_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(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 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& 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& childElem(children.item(i)); + Arabica::DOM::Element 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& childElem(children.item(i)); + Arabica::DOM::Element 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& 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& childElem(children.item(i)); + Arabica::DOM::Element 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 > _stateNames; Arabica::XPath::NodeSet _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 #include // trim -#define SCXML_VERBOSE 1 +//#define SCXML_VERBOSE + +#include "uscxml/config.h" + +#ifdef APPLE +#include +#include +#include +#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(), 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(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 + +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 #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(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" -- cgit v0.12