#include #include // malloc #include // assert #include // printf #include // stringstream #include // deque #include // trim #include #define USCXML_VERBOSE //#define WITH_DM_ECMA_JSC #include "uscxml/config.h" #ifdef APPLE #include #include #include #endif #ifndef AUTOINCLUDE_TEST #include "test-c-machine.scxml.c" //#include "/Users/sradomski/Documents/TK/Code/uscxml/build/cli/test/gen/c/lua/test192.scxml.machine.c" #endif //#include "uscxml/util/URL.h" //#include "uscxml/concurrency/Timer.h" //#include "uscxml/dom/DOMUtils.h" #include "uscxml/plugins/Factory.h" #include "uscxml/plugins/IOProcessorImpl.h" #include "uscxml/plugins/InvokerImpl.h" //#include "uscxml/Interpreter.h" #include "uscxml/util/UUID.h" //#include "uscxml/server/HTTPServer.h" //#include "uscxml/plugins/invoker/dirmon/DirMonInvoker.h" #include "uscxml/plugins/datamodel/promela/PromelaDataModel.h" #ifdef FEATS_ON_CMD #ifdef WITH_DM_ECMA_V8 #include "uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h" #endif #ifdef WITH_DM_ECMA_JSC #include "uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h" #endif #ifdef WITH_DM_LUA #include "uscxml/plugins/datamodel/lua/LuaDataModel.h" #endif #endif #include "uscxml/interpreter/InterpreterImpl.h" #include "uscxml/interpreter/BasicEventQueue.h" #include "uscxml/interpreter/BasicDelayedEventQueue.h" #ifdef BUILD_PROFILING # include "uscxml/plugins/DataModel.h" # endif #define USER_DATA(ctx) ((StateMachine*)(((uscxml_ctx*)ctx)->user_data)) using namespace uscxml; namespace XERCESC_NS { class DOMDocument; class DOMNode; } class StateMachine : public DataModelCallbacks, public IOProcessorCallbacks, public DelayedEventQueueCallbacks, public InvokerCallbacks { public: StateMachine(const uscxml_machine* machine) : machine(machine), parentMachine(NULL), topMostMachine(NULL), invocation(NULL) { allMachines[sessionId] = this; topMostMachine = this; currentMachine = allMachines.begin(); try { init(); } catch (ErrorEvent e) { LOGD(USCXML_FATAL) << e; } } StateMachine(StateMachine* parent, const uscxml_machine* machine, const uscxml_elem_invoke* invoke) : machine(machine), invocation(invoke) { parentMachine = parent; topMostMachine = parent->topMostMachine; init(); } ActionLanguage* getActionLanguage() { return NULL; } std::set getMonitors() { return std::set(); } std::string getBaseURL() { return ""; } virtual Logger getLogger() { return Logger::getDefault(); } const std::string& getName() { return name; } const std::string& getSessionId() { return sessionId; } const std::map& getIOProcessors() { return ioProcs; } void enqueueInternal(const Event& event) { iq.push_back(event); } void enqueueExternal(const Event& event) { eq.push_back(event); } void enqueueAtInvoker(const std::string& invokeId, const Event& event) { if (invokers.find(invokeId) != invokers.end()) invokers[invokeId].eventFromSCXML(event); } void enqueueAtParent(const Event& event) { if (parentMachine != NULL) parentMachine->enqueueExternal(event); } bool isInState(const std::string& stateId) { for (size_t i = 0; i < ctx.machine->nr_states; i++) { if (ctx.machine->states[i].name && strcmp(ctx.machine->states[i].name, stateId.c_str()) == 0 && BIT_HAS(i, ctx.config)) { return true; } } return false; } XERCESC_NS::DOMDocument* getDocument() const { return NULL; } const std::map& getInvokers() { return invokers; } void init() { sessionId = uscxml::UUID::getUUID(); isFinalized = false; // clear and initialize machine context memset(&ctx, 0, sizeof(uscxml_ctx)); ctx.machine = machine; ctx.user_data = (void*)this; // register callbacks with scxml context ctx.is_matched = &isMatched; ctx.is_true = &isTrue; ctx.raise_done_event = &raiseDoneEvent; ctx.invoke = &invoke; ctx.exec_content_send = &execContentSend; ctx.exec_content_raise = &execContentRaise; ctx.exec_content_cancel = &execContentCancel; ctx.exec_content_log = &execContentLog; ctx.exec_content_assign = &execContentAssign; ctx.exec_content_foreach_init = &execContentForeachInit; ctx.exec_content_foreach_next = &execContentForeachNext; ctx.exec_content_foreach_done = &execContentForeachDone; ctx.dequeue_external = &dequeueExternal; ctx.dequeue_internal = &dequeueInternal; ctx.exec_content_init = &execContentInit; ctx.exec_content_script = &execContentScript; name = machine->name; // register IO Procs std::map allIOProcs = Factory::getInstance()->getIOProcessors(); for (auto ioProcImpl : allIOProcs) { ioProcs[ioProcImpl.first] = Factory::getInstance()->createIOProcessor(ioProcImpl.first, this); std::list names = ioProcImpl.second->getNames(); for (auto name : names) { ioProcs[name] = ioProcs[ioProcImpl.first]; } } delayQueue = DelayedEventQueue(std::shared_ptr(new BasicDelayedEventQueue(this))); dataModel = Factory::getInstance()->createDataModel(machine->datamodel, this); if (invocation != NULL) { /// test 226/240 - initialize from invoke request if (invocation->params != NULL) { const uscxml_elem_param* param = invocation->params; while(USCXML_ELEM_PARAM_IS_SET(param)) { std::string identifier; if (param->name != NULL) { identifier = param->name; } else if (param->location != NULL) { identifier = param->location; } invokeData[identifier] = parentMachine->dataModel.evalAsData(param->expr); param++; } } if (invocation->namelist != NULL) { const char* cPtr = invocation->namelist; const char* aPtr = invocation->namelist; while(cPtr) { while (isspace(*cPtr)) cPtr++; aPtr = cPtr; while(*cPtr && !isspace(*cPtr)) cPtr++; if (aPtr == cPtr) break; std::string identifier = std::string(aPtr, cPtr - aPtr); invokeData[identifier] = parentMachine->dataModel.evalAsData(identifier); } } } } virtual ~StateMachine() { if (parentMachine != NULL) { topMostMachine->allMachines.erase(topMostMachine->invocationIds[invocation]); } // finalize(); delayQueue.cancelAllDelayed(); while(eq.size() > 0) { eq.pop_front(); } eq.clear(); while(iq.size() > 0) { iq.pop_front(); } iq.clear(); } bool hasPendingWork() { return (iq.size() > 0 || eq.size() > 0 || ctx.flags & USCXML_CTX_SPONTANEOUS || ctx.flags == USCXML_CTX_PRISTINE || memcmp(ctx.config, ctx.invocations, sizeof(ctx.config)) != 0); } bool isDone() { return ctx.flags & USCXML_CTX_FINISHED; } void finalize() { if (isFinalized) return; delayQueue.cancelAllDelayed(); if (parentMachine != NULL) { std::lock_guard lock(mutex); Event done; done.invokeid = invokeId; done.name = "done.invoke." + invokeId; parentMachine->eq.push_back(done); } isFinalized = true; } void reset() { delayQueue.cancelAllDelayed(); while(eq.size() > 0) { eq.pop_front(); } while(iq.size() > 0) { iq.pop_front(); } iq.clear(); eq.clear(); init(); } int step() { // advance current machine if there are multiple currentMachine++; if (currentMachine == allMachines.end()) currentMachine = allMachines.begin(); StateMachine* toRun = currentMachine->second; if (!toRun->hasPendingWork()) { return USCXML_ERR_IDLE; } // test 187 if (toRun->isDone()) { toRun->finalize(); return USCXML_ERR_IDLE; } state = uscxml_step(&toRun->ctx); return state; } // callbacks for scxml context static int isMatched(const uscxml_ctx* ctx, const uscxml_transition* t, const void* e) { Event* event = (Event*)e; return (nameMatch(t->event, event->name.c_str())); } static int isTrue(const uscxml_ctx* ctx, const char* expr) { try { return USER_DATA(ctx)->dataModel.evalAsBool(expr); } catch (Event e) { execContentRaise(ctx, e.name.c_str()); } return false; } static int invoke(const uscxml_ctx* ctx, const uscxml_state* s, const uscxml_elem_invoke* invocation, unsigned char uninvoke) { std::map &allMachines = USER_DATA(ctx)->topMostMachine->allMachines; StateMachine* topMachine = USER_DATA(ctx)->topMostMachine; if (uninvoke) { if (invocation->machine != NULL) { if (topMachine->invocationIds.find(invocation) != topMachine->invocationIds.end() && allMachines.find(topMachine->invocationIds[invocation]) != allMachines.end()) { delete allMachines[topMachine->invocationIds[invocation]]; topMachine->allMachines.erase(topMachine->invocationIds[invocation]); topMachine->invocationIds.erase(invocation); } } else { // TODO: Uninvoke other types of invokers return USCXML_ERR_UNSUPPORTED; } } else { // invocations if (invocation->machine != NULL) { // invoke a nested SCXML machine StateMachine* invokedMachine = NULL; try { invokedMachine = new StateMachine(USER_DATA(ctx), invocation->machine, invocation); } catch (Event e) { delete invokedMachine; return USCXML_ERR_EXEC_CONTENT; } if (invocation->id != NULL) { invokedMachine->invokeId = invocation->id; } else if (invocation->idlocation != NULL) { // test224 invokedMachine->invokeId = (invocation->sourcename != NULL ? std::string(invocation->sourcename) + "." : "") + uscxml::UUID::getUUID(); USER_DATA(ctx)->dataModel.assign(invocation->idlocation, Data(invokedMachine->invokeId, Data::VERBATIM)); } else { invokedMachine->invokeId = uscxml::UUID::getUUID(); } allMachines[invokedMachine->invokeId] = invokedMachine; topMachine->invocationIds[invocation] = invokedMachine->invokeId; } else if (Factory::getInstance()->hasInvoker(invocation->type)) { Event invokeEvent; // see BasicContentExecutor::384ff // TODO: Establish the invokeEvent if (invocation->params != NULL) { } Invoker inv = Factory::getInstance()->createInvoker(invocation->type, USER_DATA(ctx)); inv.invoke("", invokeEvent); USER_DATA(ctx)->_invocations[invocation] = inv; } else { return USCXML_ERR_UNSUPPORTED; } } return USCXML_ERR_OK; } static int raiseDoneEvent(const uscxml_ctx* ctx, const uscxml_state* state, const uscxml_elem_donedata* donedata) { Event e; e.name = std::string("done.state.") + state->name; if (donedata) { if (donedata->content != NULL) { if (isNumeric(donedata->content, 10)) { // test 529 e.data = Data(strTo(donedata->content), Data::INTERPRETED); } else { e.data = Data(donedata->content, Data::VERBATIM); } } else if (donedata->contentexpr != NULL) { try { e.data = USER_DATA(ctx)->dataModel.getAsData(donedata->contentexpr); } catch (Event e) { execContentRaise(ctx, e.name.c_str()); } } else { try { const uscxml_elem_param* param = donedata->params; while (param && USCXML_ELEM_PARAM_IS_SET(param)) { Data paramValue; if (param->expr != NULL) { paramValue = USER_DATA(ctx)->dataModel.evalAsData(param->expr); } else if(param->location) { paramValue = USER_DATA(ctx)->dataModel.evalAsData(param->location); } e.params.insert(std::make_pair(param->name, paramValue)); param++; } } catch (Event e) { execContentRaise(ctx, e.name.c_str()); } } } #ifdef USCXML_VERBOSE printf("Raising Done Event: %s\n", e.name.c_str()); #endif USER_DATA(ctx)->iq.push_back(e); return USCXML_ERR_OK; } static int execContentSend(const uscxml_ctx* ctx, const uscxml_elem_send* send) { Event e; std::string sendid; if (send->id != NULL) { sendid = send->id; } else { sendid = uscxml::UUID::getUUID(); if (send->idlocation != NULL) { USER_DATA(ctx)->dataModel.assign(send->idlocation, Data(sendid, Data::VERBATIM)); } else { e.hideSendId = true; } } e.sendid = sendid; std::string target; if (send->target != NULL) { target = send->target; } else if (send->targetexpr != NULL) { target = USER_DATA(ctx)->dataModel.evalAsData(send->targetexpr).atom; } else { target = "#_external"; } if (target.size() > 0 && (target[0] != '#' || target[1] != '_')) { std::cerr << "Target '" << target << "' is not supported yet" << std::endl; e.name = "error.execution"; execContentRaise(ctx, e); return USCXML_ERR_INVALID_TARGET; } e.origintype = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor"; std::string type; try { if (send->type != NULL) { type = send->type; } else if (send->typeexpr != NULL) { type = USER_DATA(ctx)->dataModel.evalAsData(send->typeexpr).atom; } else { type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor"; } } catch (Event exc) { e.name = "error.execution"; execContentRaise(ctx, e); return USCXML_ERR_EXEC_CONTENT; } // only one somewhat supported if (type != "http://www.w3.org/TR/scxml/#SCXMLEventProcessor") { e.name = "error.execution"; execContentRaise(ctx, e); return USCXML_ERR_INVALID_TARGET; } e.origintype = type; e.origin = "#_scxml_" + USER_DATA(ctx)->sessionId; e.invokeid = USER_DATA(ctx)->invokeId; if (send->eventexpr != NULL) { e.name = USER_DATA(ctx)->dataModel.evalAsData(send->eventexpr).atom; } else { e.name = send->event; } try { const uscxml_elem_param* param = send->params; while (param && USCXML_ELEM_PARAM_IS_SET(param)) { Data paramValue; if (param->expr != NULL) { paramValue = USER_DATA(ctx)->dataModel.evalAsData(param->expr); } else if(param->location) { paramValue = USER_DATA(ctx)->dataModel.evalAsData(param->location); } e.params.insert(std::make_pair(param->name, paramValue)); param++; } } catch (Event e) { execContentRaise(ctx, e.name.c_str()); return USCXML_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.evalAsData(key))); if (*ePtr == '\0') break; bPtr = ++ePtr; } } } } catch (Event e) { execContentRaise(ctx, e.name.c_str()); return USCXML_ERR_EXEC_CONTENT; } if (send->content != NULL) { try { // will it parse as json? Data d = USER_DATA(ctx)->dataModel.getAsData(send->content); if (!d.empty()) { e.data = d; } } catch (Event err) { e.data = Data(spaceNormalize(send->content), Data::VERBATIM); } } size_t delayMs = 0; if (send->delayexpr != NULL) { std::string delay = USER_DATA(ctx)->dataModel.evalAsData(send->delayexpr).atom; if (delay.size() > 0) { boost::trim(delay); NumAttr delayAttr(delay); if (iequals(delayAttr.unit, "ms")) { delayMs = strTo(delayAttr.value); } else if (iequals(delayAttr.unit, "s")) { delayMs = strTo(delayAttr.value) * 1000; } else if (delayAttr.unit.length() == 0) { // unit less delay is interpreted as milliseconds delayMs = strTo(delayAttr.value); } else { std::cerr << "Cannot make sense of delay value " << delay << ": does not end in 's' or 'ms'"; } } } else if (send->delay > 0) { delayMs = send->delay; } if (USER_DATA(ctx)->invokeId.size() > 0) { e.invokeid = USER_DATA(ctx)->invokeId; } USER_DATA(ctx)->sendUUIDs[e.getUUID()] = std::make_tuple(e.sendid, target, type); if (delayMs > 0) { USER_DATA(ctx)->delayQueue.enqueueDelayed(e, delayMs, e.getUUID()); } else { USER_DATA(ctx)->eventReady(e, e.getUUID()); } return USCXML_ERR_OK; } static int execContentRaise(const uscxml_ctx* ctx, Event& e) { if (boost::starts_with(e.name, "error.")) { e.eventType = Event::PLATFORM; } else { e.eventType = Event::INTERNAL; } USER_DATA(ctx)->iq.push_back(e); return USCXML_ERR_OK; } static int execContentRaise(const uscxml_ctx* ctx, const char* event) { Event e; e.name = event; return execContentRaise(ctx, e); } static int execContentCancel(const uscxml_ctx* ctx, const char* sendid, const char* sendidexpr) { std::string eventId; if (sendid != NULL) { eventId = sendid; } else if (sendidexpr != NULL) { eventId = USER_DATA(ctx)->dataModel.evalAsData(sendidexpr).atom; } if (eventId.length() > 0) { // find all events with given id for (auto evIter = USER_DATA(ctx)->sendUUIDs.begin(); evIter != USER_DATA(ctx)->sendUUIDs.end(); evIter++) { std::string sendid = std::get<0>(evIter->second); if (eventId == sendid) { USER_DATA(ctx)->delayQueue.cancelDelayed(evIter->first); } } } else { execContentRaise(ctx, "error.execution"); return USCXML_ERR_EXEC_CONTENT; } return USCXML_ERR_OK; } static int execContentLog(const uscxml_ctx* ctx, const char* label, const char* expr) { try { if (label != NULL) { printf("%s%s", label, (expr != NULL ? ": " : "")); } if (expr != NULL) { std::string msg = USER_DATA(ctx)->dataModel.evalAsData(expr).atom; printf("%s", msg.c_str()); } if (label != NULL || expr != NULL) { printf("\n"); } } catch (Event e) { execContentRaise(ctx, e.name.c_str()); return USCXML_ERR_EXEC_CONTENT; } return USCXML_ERR_OK; } static int execContentAssign(const uscxml_ctx* ctx, const uscxml_elem_assign* assign) { std::string key = assign->location; if (key == "_sessionid" || key == "_name" || key == "_ioprocessors" || key == "_invokers" || key == "_event") { execContentRaise(ctx, "error.execution"); return USCXML_ERR_EXEC_CONTENT; } try { // Data d = USER_DATA(ctx)->dataModel.getStringAsData(expr); if (assign->expr != NULL) { // USER_DATA(ctx)->dataModel.assign(key, // USER_DATA(ctx)->dataModel.evalAsData(assign->expr)); USER_DATA(ctx)->dataModel.assign(key, Data(assign->expr, Data::INTERPRETED)); } else if (assign->content != NULL) { Data d = Data(assign->content, Data::INTERPRETED); USER_DATA(ctx)->dataModel.assign(key, d); } } catch (Event e) { execContentRaise(ctx, e.name.c_str()); return USCXML_ERR_EXEC_CONTENT; } return USCXML_ERR_OK; } static int execContentForeachInit(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach) { try { scxml_foreach_info* feInfo = (scxml_foreach_info*)malloc(sizeof(scxml_foreach_info)); USER_DATA(ctx)->foreachInfo[foreach] = feInfo; feInfo->iterations = USER_DATA(ctx)->dataModel.getLength(foreach->array); feInfo->currIteration = 0; } catch (Event e) { execContentRaise(ctx, e.name.c_str()); return USCXML_ERR_EXEC_CONTENT; } return USCXML_ERR_OK; } static int execContentForeachNext(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach) { try { scxml_foreach_info* feInfo = USER_DATA(ctx)->foreachInfo[foreach]; if (feInfo->currIteration < feInfo->iterations) { USER_DATA(ctx)->dataModel.setForeach((foreach->item != NULL ? foreach->item : ""), (foreach->array != NULL ? foreach->array : ""), (foreach->index != NULL ? foreach->index : ""), feInfo->currIteration); feInfo->currIteration++; return USCXML_ERR_OK; } } catch (Event e) { execContentRaise(ctx, e.name.c_str()); free(USER_DATA(ctx)->foreachInfo[foreach]); USER_DATA(ctx)->foreachInfo.erase(foreach); return USCXML_ERR_EXEC_CONTENT; } return USCXML_ERR_FOREACH_DONE; } static int execContentForeachDone(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach) { free(USER_DATA(ctx)->foreachInfo[foreach]); USER_DATA(ctx)->foreachInfo.erase(foreach); return USCXML_ERR_OK; } static int execContentInit(const uscxml_ctx* ctx, const uscxml_elem_data* data) { while(USCXML_ELEM_DATA_IS_SET(data)) { if (USER_DATA(ctx)->invokeData.find(data->id) != USER_DATA(ctx)->invokeData.end()) { // passed via param or namelist: test245 try { USER_DATA(ctx)->dataModel.init(data->id, USER_DATA(ctx)->invokeData[data->id]); } catch (Event e) { execContentRaise(ctx, e.name.c_str()); } } else { Data d; std::stringstream content; try { if (data->expr != NULL) { d = Data(data->expr, Data::INTERPRETED); // d = USER_DATA(ctx)->dataModel.evalAsData(data->expr); } else if (data->content != NULL || data->src != NULL) { if (data->content) { content << data->content; } else { // avoid dependency on URL.cpp -> urlparser -> curl #if 0 URL sourceURL(data->src); if (USER_DATA(ctx)->baseURL.size() > 0) { sourceURL = URL::resolve(sourceURL, USER_DATA(ctx)->baseURL); } else { sourceURL = URL::resolveWithCWD(sourceURL); } content << sourceURL.getInContent(); #endif } /** * first attempt to parse as structured data, we will try * as space normalized string literals if this fails below */ d = USER_DATA(ctx)->dataModel.getAsData(content.str()); if (d.empty()) { d = Data(escape(spaceNormalize(content.str())), Data::VERBATIM); } } else { // leave d undefined } // this might fail with an unquoted string literal in content USER_DATA(ctx)->dataModel.init(data->id, d); } catch (Event e) { if (content.str().size() > 0) { try { d = Data(escape(spaceNormalize(content.str())), Data::VERBATIM); USER_DATA(ctx)->dataModel.init(data->id, d); } catch (Event e) { execContentRaise(ctx, e.name.c_str()); } } else { execContentRaise(ctx, e.name.c_str()); } } } data++; } return USCXML_ERR_OK; } static int execContentScript(const uscxml_ctx* ctx, const char* src, const char* content) { if (content != NULL) { USER_DATA(ctx)->dataModel.evalAsData(content); } else if (src != NULL) { return USCXML_ERR_UNSUPPORTED; } return USCXML_ERR_OK; } static void* dequeueExternal(const uscxml_ctx* ctx) { std::lock_guard lock(USER_DATA(ctx)->mutex); if (USER_DATA(ctx)->eq.size() == 0) return NULL; // set event USER_DATA(ctx)->currEvent = USER_DATA(ctx)->eq.front(); USER_DATA(ctx)->eq.pop_front(); // get an alias const Event& e = USER_DATA(ctx)->currEvent; USER_DATA(ctx)->dataModel.setEvent(e); std::map& allMachines = USER_DATA(ctx)->topMostMachine->allMachines; if (e.invokeid.size() > 0 && allMachines.find(e.invokeid) != allMachines.end()) { // we need to check for finalize content StateMachine* invokedMachine = allMachines[e.invokeid]; if (invokedMachine->invocation != NULL && invokedMachine->invocation->finalize != NULL) invokedMachine->invocation->finalize(ctx, invokedMachine->invocation, &e); } // auto forward event for (std::map::iterator machIter = allMachines.begin(); machIter != allMachines.end(); machIter++) { if (machIter->second->parentMachine != NULL && machIter->second->parentMachine == USER_DATA(ctx) && machIter->second->invocation->autoforward) { std::lock_guard lock(machIter->second->mutex); Event e2(e); machIter->second->eq.push_back(e2); } } #ifdef USCXML_VERBOSE printf("Popping External Event: %s\n", e.name.c_str()); #endif return &USER_DATA(ctx)->currEvent; } static void* dequeueInternal(const uscxml_ctx* ctx) { if (USER_DATA(ctx)->iq.size() == 0) return NULL; // set event USER_DATA(ctx)->currEvent = USER_DATA(ctx)->iq.front(); USER_DATA(ctx)->iq.pop_front(); // get an alias const Event& e = USER_DATA(ctx)->currEvent; USER_DATA(ctx)->dataModel.setEvent(e); #ifdef USCXML_VERBOSE printf("Popping Internal Event: %s\n", e.name.c_str()); #endif return &USER_DATA(ctx)->currEvent; } void eventReady(Event& e, const std::string& eventUUID) { std::lock_guard lock(mutex); //std::make_tuple(e.sendid, target, type); std::string sendid = std::get<0>(sendUUIDs[e.getUUID()]); std::string target = std::get<1>(sendUUIDs[e.getUUID()]); std::string type = std::get<2>(sendUUIDs[e.getUUID()]); if (target == "#_internal") { e.eventType = Event::INTERNAL; #ifdef USCXML_VERBOSE printf("Pushing Internal Event: %s\n", e.name.c_str()); #endif iq.push_back(e); } else if (target == "#_external") { e.eventType = Event::EXTERNAL; #ifdef USCXML_VERBOSE printf("Pushing External Event: %s\n", e.name.c_str()); #endif eq.push_back(e); } else if (target == "#_parent") { e.eventType = Event::EXTERNAL; if (parentMachine != NULL) { parentMachine->eq.push_back(e); } // TODO: handle invalid parent } else if (target.substr(0,8) == "#_scxml_") { std::string sessionId = target.substr(8); bool sessionFound = false; for (std::map::iterator machIter = topMostMachine->allMachines.begin(); machIter != topMostMachine->allMachines.end(); machIter++) { if (machIter->second->sessionId == sessionId) { e.eventType = Event::EXTERNAL; machIter->second->eq.push_back(e); sessionFound = true; break; } } if (!sessionFound) { // test496 execContentRaise(&ctx, "error.communication"); } } else if (target.substr(0,2) == "#_") { e.eventType = Event::EXTERNAL; std::string targetId = target.substr(2); if (topMostMachine->allMachines.find(targetId) != topMostMachine->allMachines.end()) { topMostMachine->allMachines[targetId]->eq.push_back(e); } else { execContentRaise(&ctx, "error.communication"); } } else { assert(false); } monitor.notify_all(); } static std::string spaceNormalize(const std::string& text) { std::stringstream content; std::string seperator; size_t start = 0; for (size_t 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(); } // TODO: isolate InterpreterImpl to reduce header deps on libxml/parser.h static bool nameMatch(const std::string& eventDescs, const std::string& eventName) { if(eventDescs.length() == 0 || eventName.length() == 0) return false; // naive case of single descriptor and exact match if (iequals(eventDescs, eventName)) return true; size_t start = 0; std::string eventDesc; for (size_t i = 0; i < eventDescs.size(); i++) { if (isspace(eventDescs[i])) { if (i > 0 && start < i - 1) { eventDesc = eventDescs.substr(start, i - start); } while(isspace(eventDescs[++i])); // skip whitespaces start = i; } else if (i + 1 == eventDescs.size()) { eventDesc = eventDescs.substr(start, i + 1 - start); } if (eventDesc.size() > 0) { // remove optional trailing .* for CCXML compatibility if (eventDesc.find("*", eventDesc.size() - 1) != std::string::npos) eventDesc = eventDesc.substr(0, eventDesc.size() - 1); if (eventDesc.find(".", eventDesc.size() - 1) != std::string::npos) eventDesc = eventDesc.substr(0, eventDesc.size() - 1); // was eventDesc the * wildcard if (eventDesc.size() == 0) return true; // eventDesc has to be a real prefix of event now and therefore shorter if (eventDesc.size() > eventName.size()) goto NEXT_DESC; // are they already equal? if (iequals(eventDesc, eventName)) return true; if (eventName.find(eventDesc) == 0) { if (eventName.find(".", eventDesc.size()) == eventDesc.size()) return true; } NEXT_DESC: eventDesc = ""; } } return false; } Event currEvent; std::map invocationIds; std::map allMachines; bool isFinalized; int state; uscxml_ctx ctx; const uscxml_machine* machine; StateMachine* parentMachine; StateMachine* topMostMachine; std::map::iterator currentMachine; // next machine to advance std::string baseURL; std::string sessionId; std::string name; // in case we were invoked std::string invokeId; const uscxml_elem_invoke* invocation; std::map invokeData; std::map _invocations; std::deque iq; std::deque eq; DataModel dataModel; std::map ioProcs; std::map invokers; protected: struct scxml_foreach_info { size_t iterations; size_t currIteration; }; // X xmlPrefix; // XERCESC_NS::DOMDocument* document; DelayedEventQueue delayQueue; std::map > sendUUIDs; std::map foreachInfo; std::condition_variable monitor; std::mutex mutex; }; int main(int argc, char** argv) { int err = 0; size_t benchmarkRuns = 1; const char* envBenchmarkRuns = getenv("USCXML_BENCHMARK_ITERATIONS"); if (envBenchmarkRuns != NULL) { benchmarkRuns = strTo(envBenchmarkRuns); } // start the webserver for the basichttp tests // HTTPServer::getInstance(3453, 3454, NULL); size_t remainingRuns = benchmarkRuns; size_t microSteps = 0; #ifdef FEATS_ON_CMD Factory::getInstance()->registerDataModel(new PromelaDataModel()); #ifdef WITH_DM_ECMA_V8 Factory::getInstance()->registerDataModel(new V8DataModel()); #endif #ifdef WITH_DM_ECMA_JSC Factory::getInstance()->registerDataModel(new JSCDataModel()); #endif #ifdef WITH_DM_LUA Factory::getInstance()->registerDataModel(new LuaDataModel()); #endif #endif StateMachine rootMachine(&USCXML_MACHINE); while(remainingRuns-- > 0) { microSteps = 0; for (;;) { err = rootMachine.step(); if (rootMachine.isDone()) break; microSteps++; } microSteps++; assert(rootMachine.ctx.flags & USCXML_CTX_TOP_LEVEL_FINAL); size_t passIdx = 0; for (size_t i = 0; i < rootMachine.ctx.machine->nr_states; i++) { if (rootMachine.ctx.machine->states[i].name && strcmp(rootMachine.ctx.machine->states[i].name, "pass") == 0) { passIdx = i; break; } } if(!BIT_HAS(passIdx, rootMachine.ctx.config)) { std::cerr << "Interpreter did not end in pass" << std::endl; exit(EXIT_FAILURE); } rootMachine.reset(); } return EXIT_SUCCESS; }