diff options
author | Stefan Radomski <github@mintwerk.de> | 2017-08-02 06:04:38 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-02 06:04:38 (GMT) |
commit | 97e72c5cb5e70eca231f74f01097c9b2392b4c98 (patch) | |
tree | f90d4ceb9d48fdc0dc240fce031dc81308e471dd /src/uscxml/plugins/datamodel/ecmascript/v8/032317 | |
parent | 998e624aaddb237767f5d80e0ff12eb2cfdc814b (diff) | |
parent | 045bde78c0587316e0373c7698413412d0f315f9 (diff) | |
download | uscxml-97e72c5cb5e70eca231f74f01097c9b2392b4c98.zip uscxml-97e72c5cb5e70eca231f74f01097c9b2392b4c98.tar.gz uscxml-97e72c5cb5e70eca231f74f01097c9b2392b4c98.tar.bz2 |
Merge pull request #165 from tklab-tud/sradomski
Reenabled V8, Debugger tests and smaller fixes
Diffstat (limited to 'src/uscxml/plugins/datamodel/ecmascript/v8/032317')
-rw-r--r-- | src/uscxml/plugins/datamodel/ecmascript/v8/032317/V8DataModel.cpp | 898 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/ecmascript/v8/032317/V8DataModel.h | 127 |
2 files changed, 1025 insertions, 0 deletions
diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/032317/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/032317/V8DataModel.cpp new file mode 100644 index 0000000..c245b67 --- /dev/null +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/032317/V8DataModel.cpp @@ -0,0 +1,898 @@ +/** + * @file + * @author 2012-2017 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +/* + * This file will compile for v8 version 3.23.17 as it was disributed e.g. by MacPorts for a while. + * + * Later v8 changed API considerably, have a look at the node.js(!) documentation for an overview: + * http://strongloop.com/strongblog/node-js-v0-12-c-apis-breaking/ + */ + +#include "uscxml/Common.h" +#include "uscxml/util/URL.h" +#include "uscxml/util/String.h" + +#include "V8DataModel.h" + +#include "uscxml/messages/Event.h" +#ifndef NO_XERCESC +#include "uscxml/util/DOM.h" +#endif +#include "uscxml/interpreter/Logging.h" + +#include <boost/algorithm/string.hpp> + +#ifdef BUILD_AS_PLUGINS +#include <Pluma/Connector.hpp> +#endif + +using namespace XERCESC_NS; + +#define SWIG_V8_VERSION 0x032317 + +#ifndef NO_XERCESC +static v8::Local<v8::Value> XMLString2JS(const XMLCh* input) { + char* res = XERCESC_NS::XMLString::transcode(input); + v8::Local<v8::Value> handle = v8::String::New(res); + free(res); + return handle; +} + +static XMLCh* JS2XMLString(const v8::Local<v8::Value>& value) { + v8::String::AsciiValue s(value); + XMLCh* ret = XERCESC_NS::XMLString::transcode(*s); + return(ret); +} + +// this is the version we support here + +#include "../V8DOM.cpp.inc" +#else +#include "../V8Event.cpp.inc" +#endif + +namespace uscxml { + +#ifdef BUILD_AS_PLUGINS +PLUMA_CONNECTOR +bool pluginConnect(pluma::Host& host) { + host.add( new V8DataModelProvider() ); + return true; +} +#endif + +V8DataModel::V8DataModel() { +// _contexts.push_back(v8::Context::New()); +} + +V8DataModel::~V8DataModel() { + _context.Dispose(); +// if (_isolate != NULL) { +// _isolate->Dispose(); +// } +} + +void V8DataModel::addExtension(DataModelExtension* ext) { +#if 0 + if (_extensions.find(ext) != _extensions.end()) + return; + + ext->dm = this; + _extensions.insert(ext); + + v8::Locker locker; + v8::HandleScope scope; + v8::Context::Scope contextScope(_contexts.front()); + v8::Local<v8::Object> currScope = _contexts.front()->Global(); + + std::list<std::string> locPath = tokenize(ext->provides(), '.'); + std::list<std::string>::iterator locIter = locPath.begin(); + while(true) { + std::string pathComp = *locIter; + v8::Local<v8::String> pathCompJS = v8::String::New(locIter->c_str()); + + if (++locIter != locPath.end()) { + // just another intermediate step + if (!currScope->Has(pathCompJS)) { + currScope->Set(pathCompJS, v8::Object::New()); + } + + v8::Local<v8::Value> newScope = currScope->Get(pathCompJS); + if (newScope->IsObject()) { + currScope = newScope->ToObject(); + } else { + throw "Cannot add datamodel extension in non-object"; + } + } else { + // this is the function! + currScope->Set(pathCompJS, v8::FunctionTemplate::New(jsExtension, v8::External::New(reinterpret_cast<void*>(ext)))->GetFunction(), v8::ReadOnly); + break; + } + } +#endif +} + +void V8DataModel::jsExtension(const v8::FunctionCallbackInfo<v8::Value>& info) { +#if 0 + DataModelExtension* extension = static_cast<DataModelExtension*>(v8::External::Unwrap(args.Data())); + + v8::Local<v8::String> memberJS; + std::string memberName; + + if (args.Length() > 0 && args[0]->IsString()) { + memberJS = args[0]->ToString(); + memberName = *v8::String::AsciiValue(memberJS); + } + + if (args.Length() > 1) { + // setter + Data data = ((V8DataModel*)(extension->dm))->getValueAsData(args[1]); + extension->setValueOf(memberName, data); + return v8::Undefined(); + } + + if (args.Length() == 1) { + // getter + return ((V8DataModel*)(extension->dm))->getDataAsValue(extension->getValueOf(memberName)); + } + return v8::Undefined(); +#endif +} + +std::mutex V8DataModel::_initMutex; + +v8::Isolate* V8DataModel::_isolate = NULL; + +#ifndef NO_XERCESC +void V8NodeListIndexedPropertyHandler(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { + XERCESC_NS::DOMNodeList* list; + SWIG_V8_GetInstancePtr(info.Holder(), (void**)&list); + + if (list->getLength() >= index) { + XERCESC_NS::DOMNode* node = list->item(index); + + v8::Handle<v8::Value> val = SWIG_NewPointerObj(SWIG_as_voidptr(node), SWIG_TypeDynamicCast(SWIGTYPE_p_XERCES_CPP_NAMESPACE__DOMNode, SWIG_as_voidptrptr(&node)), 0 | 0 ); + info.GetReturnValue().Set(val); + return; + } + info.GetReturnValue().Set(v8::Undefined()); + +} +#endif + +std::shared_ptr<DataModelImpl> V8DataModel::create(DataModelCallbacks* callbacks) { + std::shared_ptr<V8DataModel> dm(new V8DataModel()); + dm->_callbacks = callbacks; + dm->setup(); + return dm; +} + +void V8DataModel::setup() { + // TODO: we cannot use one isolate per thread as swig's type will be unknown :( + // We could register them by hand and avoid the _export_ globals in swig? + if (_isolate == NULL) { + _isolate = v8::Isolate::New(); + } + + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + + // Create a handle scope to hold the temporary references. + v8::HandleScope scope(_isolate); + + // Create a template for the global object where we set the built-in global functions. + v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); + + // some free functions + global->Set(v8::String::NewSymbol("print"), + v8::FunctionTemplate::New(_isolate, jsPrint,v8::External::New(reinterpret_cast<void*>(this)))); + global->Set(v8::String::NewSymbol("In"), + v8::FunctionTemplate::New(_isolate, jsIn, v8::External::New(reinterpret_cast<void*>(this)))); + + v8::Local<v8::Context> context = v8::Context::New(_isolate, NULL, global); + + _context.Reset(_isolate, context); + + // Enter the new context so all the following operations take place within it. + v8::Context::Scope contextScope(context); + assert(_isolate->GetCurrentContext() == context); + +#ifndef NO_XERCESC + + // not thread safe! + { + std::lock_guard<std::mutex> lock(_initMutex); + SWIGV8_INIT(context->Global()); + + // register subscript operator with nodelist + v8::Handle<v8::FunctionTemplate> _exports_DOMNodeList_class = SWIGV8_CreateClassTemplate("_exports_DOMNodeList"); + + _exports_DOMNodeList_class->InstanceTemplate()->SetIndexedPropertyHandler(V8NodeListIndexedPropertyHandler); + SWIGV8_AddMemberFunction(_exports_DOMNodeList_class, "item", _wrap_DOMNodeList_item); + SWIGV8_AddMemberFunction(_exports_DOMNodeList_class, "getLength", _wrap_DOMNodeList_getLength); + + SWIGV8_SET_CLASS_TEMPL(_exports_DOMNodeList_clientData.class_templ, _exports_DOMNodeList_class); + + } +#endif + + context->Global()->SetAccessor(v8::String::NewSymbol("_sessionid"), + V8DataModel::getAttribute, + V8DataModel::setWithException, + v8::String::New(_callbacks->getSessionId().c_str())); + context->Global()->SetAccessor(v8::String::NewSymbol("_name"), + V8DataModel::getAttribute, + V8DataModel::setWithException, + v8::String::New(_callbacks->getName().c_str())); + context->Global()->SetAccessor(v8::String::NewSymbol("_ioprocessors"), + V8DataModel::getIOProcessors, + V8DataModel::setWithException, + v8::External::New(reinterpret_cast<void*>(this))); + context->Global()->SetAccessor(v8::String::NewSymbol("_invokers"), + V8DataModel::getInvokers, + V8DataModel::setWithException, + v8::External::New(reinterpret_cast<void*>(this))); + +// v8::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value> > persistent(_isolate, context); + +#if 0 + + // instantiate the document function + v8::Local<v8::Function> docCtor = V8Document::getTmpl()->GetFunction(); + v8::Local<v8::Object> docObj = docCtor->NewInstance(); + + V8Document::V8DocumentPrivate* privData = new V8Document::V8DocumentPrivate(); + privData->nativeObj = new Document<std::string>(interpreter->getDocument()); + privData->dom = _dom; + docObj->SetInternalField(0, V8DOM::toExternal(privData)); + + context->Global()->Set(v8::String::New("document"), docObj); + + // setup constructors + context->Global()->Set(v8::String::New("ArrayBuffer"), V8ArrayBuffer::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Int8Array"), V8Int8Array::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Uint8Array"), V8Uint8Array::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Uint8ClampedArray"), V8Uint8ClampedArray::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Int16Array"), V8Int16Array::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Uint16Array"), V8Uint16Array::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Int32Array"), V8Int32Array::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Uint32Array"), V8Uint32Array::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Float32Array"), V8Float32Array::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("Float64Array"), V8Float64Array::getConstructor()->GetFunction()); + context->Global()->Set(v8::String::New("DataView"), V8DataView::getConstructor()->GetFunction()); + + + + // instantiate objects - we have to have a context for that! + eval(Element<std::string>(), "_x = {};"); +#endif + +} + +void V8DataModel::getAttribute(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + info.GetReturnValue().Set(info.Data()); +} + +void V8DataModel::setWithException(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<void>& info) { + v8::String::AsciiValue data(property); + std::string msg = "Cannot set " + std::string(*data); + v8::ThrowException(v8::Exception::ReferenceError(v8::String::New(msg.c_str()))); +} + +void V8DataModel::getIOProcessors(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Local<v8::External> field = v8::Local<v8::External>::Cast(info.Data()); + V8DataModel* dataModel = (V8DataModel*)field->Value(); + + if (dataModel->_ioProcessors.IsEmpty()) { + + v8::Local<v8::Object> ioProcs = v8::Local<v8::Object>::New(v8::Isolate::GetCurrent(), v8::Object::New()); + //v8::Local<v8::Object> ioProcessorObj = v8::Object::New(); + std::map<std::string, IOProcessor> ioProcessors = dataModel->_callbacks->getIOProcessors(); + std::map<std::string, IOProcessor>::const_iterator ioProcIter = ioProcessors.begin(); + while(ioProcIter != ioProcessors.end()) { + // std::cout << ioProcIter->first << std::endl; + ioProcs->Set(v8::String::New(ioProcIter->first.c_str()), + dataModel->getDataAsValue(ioProcIter->second.getDataModelVariables())); + ioProcIter++; + } + dataModel->_ioProcessors.Reset(v8::Isolate::GetCurrent(), ioProcs); + } + info.GetReturnValue().Set(dataModel->_ioProcessors); +} + +void V8DataModel::getInvokers(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Local<v8::External> field = v8::Local<v8::External>::Cast(info.Data()); + V8DataModel* dataModel = (V8DataModel*)field->Value(); + + if (dataModel->_invokers.IsEmpty()) { + v8::Local<v8::Object> invoks = v8::Local<v8::Object>::New(v8::Isolate::GetCurrent(), v8::Object::New()); + //v8::Local<v8::Object> ioProcessorObj = v8::Object::New(); + std::map<std::string, Invoker> invokers = dataModel->_callbacks->getInvokers(); + std::map<std::string, Invoker>::const_iterator invokerIter = invokers.begin(); + while(invokerIter != invokers.end()) { + // std::cout << ioProcIter->first << std::endl; + invoks->Set(v8::String::New(invokerIter->first.c_str()), + dataModel->getDataAsValue(invokerIter->second.getDataModelVariables())); + invokerIter++; + } + dataModel->_invokers.Reset(v8::Isolate::GetCurrent(), invoks); + + } + info.GetReturnValue().Set(dataModel->_invokers); +} + +void V8DataModel::setEvent(const Event& event) { + + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + + v8::HandleScope scope(_isolate); + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + + v8::Local<v8::Object> global = ctx->Global(); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + assert(_isolate->GetCurrentContext() == ctx); // only valid in context::scope + + +#if 0 + // this would work as swig_exports_ will get redefined per isolate + { + std::lock_guard<std::mutex> lock(_initMutex); + SWIGV8_INIT(context->Global()); + } +#endif + + Event* evPtr = new Event(event); + + v8::Local<v8::Value> eventVal = SWIG_V8_NewPointerObj(evPtr, SWIGTYPE_p_uscxml__Event, SWIG_POINTER_OWN); + v8::Local<v8::Object> eventObj = v8::Local<v8::Object>::Cast(eventVal); + + /* + v8::Local<v8::Array> properties = eventObj->GetPropertyNames(); + for (int i = 0; i < properties->Length(); i++) { + assert(properties->Get(i)->IsString()); + v8::String::AsciiValue key(v8::Local<v8::String>::Cast(properties->Get(i))); + std::cout << *key << std::endl; + } + */ + +#define V8_SET_FROMEVENT_OR_UNDEF(test, field) \ +if (test) { \ + eventObj->Set(v8::String::NewSymbol(#field),v8::String::NewFromUtf8(_isolate, event.field.c_str())); \ +} else { \ + eventObj->Set(v8::String::NewSymbol(#field),v8::Undefined(_isolate)); \ +} + + V8_SET_FROMEVENT_OR_UNDEF(event.origintype.size() > 0, origintype); // test333 + V8_SET_FROMEVENT_OR_UNDEF(event.origin.size() > 0, origin); // test335 + V8_SET_FROMEVENT_OR_UNDEF(!event.hideSendId, sendid); // test337 + V8_SET_FROMEVENT_OR_UNDEF(event.invokeid.size() > 0, invokeid); // test339 + + // test 331 + switch (event.eventType) { + case Event::EXTERNAL: + eventObj->Set(v8::String::NewSymbol("type"), v8::String::NewFromUtf8(_isolate, "external")); + break; + case Event::INTERNAL: + eventObj->Set(v8::String::NewSymbol("type"), v8::String::NewFromUtf8(_isolate, "internal")); + break; + case Event::PLATFORM: + eventObj->Set(v8::String::NewSymbol("type"), v8::String::NewFromUtf8(_isolate, "platform")); + break; + } + + if (event.data.node) { +#ifndef NO_XERCESC + eventObj->Set(v8::String::NewSymbol("data"), getNodeAsValue(event.data.node)); +#else + ERROR_EXECUTION_THROW("Compiled without DOM support"); +#endif + } else { + // _event.data is KVP + Data data = event.data; + if (!event.params.empty()) { + Event::params_t::const_iterator paramIter = event.params.begin(); + while(paramIter != event.params.end()) { + data.compound[paramIter->first] = paramIter->second; + paramIter++; + } + } + if (!event.namelist.empty()) { + Event::namelist_t::const_iterator nameListIter = event.namelist.begin(); + while(nameListIter != event.namelist.end()) { + data.compound[nameListIter->first] = nameListIter->second; + nameListIter++; + } + } + if (!data.empty()) { + eventObj->Set(v8::String::NewSymbol("data"), getDataAsValue(data)); // set data part of _event + } else { + // test 343 / test 488 + eventObj->Set(v8::String::NewSymbol("data"), v8::Undefined()); // set data part of _event + } + } + // we cannot make _event v8::ReadOnly as it will ignore subsequent setEvents + global->Set(v8::String::NewSymbol("_event"), eventObj); + +} + +Data V8DataModel::getAsData(const std::string& content) { + Data d = Data::fromJSON(content); + if (!d.empty()) + return d; + + std::string trimmed = boost::trim_copy(content); + if (trimmed.length() > 0) { + if (isNumeric(trimmed.c_str(), 10)) { + d = Data(trimmed, Data::INTERPRETED); + } else if (trimmed.length() >= 2 && + ((trimmed[0] == '"' && trimmed[trimmed.length() - 1] == '"') || + (trimmed[0] == '\'' && trimmed[trimmed.length() - 1] == '\''))) { + d = Data(trimmed.substr(1, trimmed.length() - 2), Data::VERBATIM); + } else { + // test558, test562 + ERROR_EXECUTION(e, "Given content cannot be interpreted as data"); + e.data.compound["literal"] = Data(trimmed, Data::VERBATIM); + throw e; + } + } + return d; +} + +Data V8DataModel::evalAsData(const std::string& content) { + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + + v8::HandleScope scope(_isolate); + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + v8::Local<v8::Value> result = evalAsValue(content); + Data data = getValueAsData(result); + return data; +} + +Data V8DataModel::getValueAsData(const v8::Local<v8::Value>& value) { + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + v8::HandleScope scope(_isolate); + + std::set<v8::Value*> foo = std::set<v8::Value*>(); + return getValueAsData(value, foo); +} + +Data V8DataModel::getValueAsData(const v8::Local<v8::Value>& value, std::set<v8::Value*>& alreadySeen) { + + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + Data data; + + /// TODO: Breaking cycles does not work yet + if (alreadySeen.find(*value) != alreadySeen.end()) + return data; + alreadySeen.insert(*value); + + if (false) { + } else if (value->IsArray()) { + v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(value); + for (int i = 0; i < array->Length(); i++) { + data.array.push_back(getValueAsData(array->Get(i), alreadySeen)); + } + } else if (value->IsBoolean()) { + data.atom = (value->ToBoolean()->Value() ? "true" : "false"); + } else if (value->IsBooleanObject()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsBooleanObject is unimplemented" << std::endl; + } else if (value->IsDate()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsDate is unimplemented" << std::endl; + } else if (value->IsExternal()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsExternal is unimplemented" << std::endl; + } else if (value->IsFalse()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsFalse is unimplemented" << std::endl; + } else if (value->IsFunction()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsFunction is unimplemented" << std::endl; + } else if (value->IsInt32()) { + int32_t prop = value->Int32Value(); + data.atom = toStr(prop); + } else if (value->IsNativeError()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsNativeError is unimplemented" << std::endl; + } else if (value->IsNull()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsNull is unimplemented" << std::endl; + } else if (value->IsNumber()) { + v8::String::AsciiValue prop(v8::Local<v8::String>::Cast(v8::Local<v8::Number>::Cast(value))); + data.atom = *prop; + } else if (value->IsNumberObject()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsNumberObject is unimplemented" << std::endl; + } else if (value->IsObject()) { + +// if (V8ArrayBuffer::hasInstance(value)) { +// uscxml::V8ArrayBuffer::V8ArrayBufferPrivate* privObj = V8DOM::toClassPtr<V8ArrayBuffer::V8ArrayBufferPrivate >(value->ToObject()->GetInternalField(0)); +// data.binary = privObj->nativeObj->_blob; +// return data; +// } + +#ifndef NO_XERCESC + + v8::Local<v8::FunctionTemplate> tmpl = v8::Local<v8::FunctionTemplate>::New(_isolate, _exports_DOMNode_clientData.class_templ); + if (tmpl->HasInstance(value)) { + SWIG_V8_GetInstancePtr(value, (void**)&(data.node)); + return data; + } +#endif + v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value); + v8::Local<v8::Array> properties = object->GetPropertyNames(); + for (int i = 0; i < properties->Length(); i++) { + assert(properties->Get(i)->IsString()); + v8::String::AsciiValue key(v8::Local<v8::String>::Cast(properties->Get(i))); + v8::Local<v8::Value> property = object->Get(properties->Get(i)); + data.compound[*key] = getValueAsData(property, alreadySeen); + } + } else if (value->IsRegExp()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsRegExp is unimplemented" << std::endl; + } else if(value->IsString()) { + v8::String::AsciiValue property(v8::Local<v8::String>::Cast(value)); + data.atom = *property; + data.type = Data::VERBATIM; + } else if(value->IsStringObject()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsStringObject is unimplemented" << std::endl; + } else if(value->IsTrue()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsTrue is unimplemented" << std::endl; + } else if(value->IsUint32()) { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "IsUint32 is unimplemented" << std::endl; + } else if(value->IsUndefined()) { + data.atom = "undefined"; + } else { + LOG(_callbacks->getLogger(), USCXML_ERROR) << "Value's type is unknown!" << std::endl; + } + return data; +} + +#ifndef NO_XERCESC +v8::Local<v8::Value> V8DataModel::getNodeAsValue(const XERCESC_NS::DOMNode* node) { + return SWIG_NewPointerObj(SWIG_as_voidptr(node), + SWIG_TypeDynamicCast(SWIGTYPE_p_XERCES_CPP_NAMESPACE__DOMNode, + SWIG_as_voidptrptr(&node)), + 0); +} +#endif + +v8::Local<v8::Value> V8DataModel::getDataAsValue(const Data& data) { + + if (data.compound.size() > 0) { + v8::Local<v8::Object> value = v8::Object::New(); + std::map<std::string, Data>::const_iterator compoundIter = data.compound.begin(); + while(compoundIter != data.compound.end()) { + value->Set(v8::String::NewSymbol(compoundIter->first.c_str()), getDataAsValue(compoundIter->second)); + compoundIter++; + } + return value; + } + if (data.array.size() > 0) { + v8::Local<v8::Object> value = v8::Array::New(_isolate, data.array.size()); + std::list<Data>::const_iterator arrayIter = data.array.begin(); + uint32_t index = 0; + while(arrayIter != data.array.end()) { + value->Set(index++, getDataAsValue(*arrayIter)); + arrayIter++; + } + return value; + } + if (data.atom.length() > 0) { + switch (data.type) { + case Data::VERBATIM: + return v8::String::New(data.atom.c_str()); + break; + case Data::INTERPRETED: + return evalAsValue(data.atom); + break; + } + } + if (data.node) { +#ifndef NO_XERCESC + return getNodeAsValue(data.node); +#else + ERROR_EXECUTION_THROW("Compiled without DOM support"); +#endif + } + +// if (data.binary) { +// uscxml::ArrayBuffer* arrBuffer = new uscxml::ArrayBuffer(data.binary); +// v8::Local<v8::Function> retCtor = V8ArrayBuffer::getTmpl()->GetFunction(); +// v8::Persistent<v8::Object> retObj = v8::Persistent<v8::Object>::New(retCtor->NewInstance()); +// +// struct V8ArrayBuffer::V8ArrayBufferPrivate* retPrivData = new V8ArrayBuffer::V8ArrayBufferPrivate(); +// retPrivData->nativeObj = arrBuffer; +// retObj->SetInternalField(0, V8DOM::toExternal(retPrivData)); +// +// retObj.MakeWeak(0, V8ArrayBuffer::jsDestructor); +// return retObj; +// } + + return v8::Undefined(); +} + +void V8DataModel::jsPrint(const v8::FunctionCallbackInfo<v8::Value>& info) { + if (info.Length() > 0) { + v8::String::AsciiValue printMsg(info[0]->ToString()); + v8::Local<v8::External> field = v8::Local<v8::External>::Cast(info.Data()); + V8DataModel* dataModel = (V8DataModel*)field->Value(); + dataModel->_callbacks->getLogger().log(USCXML_LOG) << *printMsg; + } +} + +void V8DataModel::jsIn(const v8::FunctionCallbackInfo<v8::Value>& info) { + v8::Local<v8::External> field = v8::Local<v8::External>::Cast(info.Data()); + V8DataModel* dataModel = (V8DataModel*)field->Value(); + + for (unsigned int i = 0; i < info.Length(); i++) { + if (info[i]->IsString()) { + std::string stateName(*v8::String::AsciiValue(info[i]->ToString())); + if (dataModel->_callbacks->isInState(stateName)) { + continue; + } + } + info.GetReturnValue().Set(false); + return; + } + info.GetReturnValue().Set(true); +} + +bool V8DataModel::isLegalDataValue(const std::string& expr) { + return isValidSyntax("var __tmp = " + expr); +} + +bool V8DataModel::isValidSyntax(const std::string& expr) { + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + v8::HandleScope scope(_isolate); + + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + v8::TryCatch tryCatch; + + v8::Local<v8::String> source = v8::String::New(expr.c_str()); + if (tryCatch.HasCaught() || source.IsEmpty()) { + return false; + } + + v8::Local<v8::Script> script = v8::Script::Compile(source); + if (tryCatch.HasCaught() || script.IsEmpty()) { + return false; + } + + return true; +} + +uint32_t V8DataModel::getLength(const std::string& expr) { + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + v8::HandleScope scope(_isolate); + + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + v8::Local<v8::Value> result = evalAsValue(expr); + if (!result.IsEmpty() && result->IsArray()) + return result.As<v8::Array>()->Length(); + + ERROR_EXECUTION_THROW("'" + expr + "' does not evaluate to an array.") +} + +void V8DataModel::setForeach(const std::string& item, + const std::string& array, + const std::string& index, + uint32_t iteration) { + if (!isDeclared(item)) { + assign(item, Data()); + } + + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + v8::HandleScope scope(_isolate); + + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + // assign array element to item + std::stringstream ss; + ss << item << " = " << array << "[" << iteration << "]"; +// assign(item, Data(ss.str(), Data::INTERPRETED)); + // test152: we need "'continue' = array[index]" to throw + evalAsValue(ss.str()); + if (index.length() > 0) { + // assign iteration element to index + std::stringstream ss; + ss << iteration; + assign(index, Data(ss.str(), Data::INTERPRETED)); + } +} + +bool V8DataModel::isDeclared(const std::string& expr) { + /** + * Undeclared variables can be checked by trying to access them and catching + * a reference error. + */ + + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + + v8::HandleScope scope(_isolate); + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + v8::Local<v8::String> source = v8::String::New(expr.c_str()); + v8::Local<v8::Script> script = v8::Script::Compile(source); + + v8::Local<v8::Value> result; + if (!script.IsEmpty()) + result = script->Run(); + + if (result.IsEmpty()) + return false; + + return true; +} + +bool V8DataModel::evalAsBool(const std::string& expr) { + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + + v8::HandleScope scope(_isolate); + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + v8::Local<v8::Value> result = evalAsValue(expr); + return(result->ToBoolean()->BooleanValue()); +} + + +void V8DataModel::assign(const std::string& location, const Data& data, const std::map<std::string, std::string>& attr) { + + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + v8::HandleScope scope(_isolate); + + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); +#ifndef NO_XERCESC + v8::Local<v8::Object> global = ctx->Global(); +#endif + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + if (location.compare("_sessionid") == 0) // test 322 + ERROR_EXECUTION_THROW("Cannot assign to _sessionId"); + if (location.compare("_name") == 0) + ERROR_EXECUTION_THROW("Cannot assign to _name"); + if (location.compare("_ioprocessors") == 0) // test 326 + ERROR_EXECUTION_THROW("Cannot assign to _ioprocessors"); + if (location.compare("_invokers") == 0) + ERROR_EXECUTION_THROW("Cannot assign to _invokers"); + if (location.compare("_event") == 0) + ERROR_EXECUTION_THROW("Cannot assign to _event"); + + if (data.node) { +#ifndef NO_XERCESC + global->Set(v8::String::NewSymbol(location.c_str()), getNodeAsValue(data.node)); +#else + ERROR_EXECUTION_THROW("Compiled without DOM support"); +#endif + } else { + evalAsValue(location + " = " + Data::toJSON(data)); + } +} + +void V8DataModel::init(const std::string& location, const Data& data, const std::map<std::string, std::string>& attr) { + v8::Locker locker(_isolate); + v8::Isolate::Scope isoScope(_isolate); + v8::HandleScope scope(_isolate); + + v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); + v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + try { + if (data.empty()) { + // test 277 + evalAsValue(location + " = undefined", true); + } else { + assign(location, data); + } + } catch (ErrorEvent e) { + // test 277 (before PROMELA init changes) + evalAsValue(location + " = undefined", true); + + // we need to get error.execution into the queue + throw e; + } +} + +v8::Local<v8::Value> V8DataModel::evalAsValue(const std::string& expr, bool dontThrow) { + +// v8::Locker locker(_isolate); +// v8::Isolate::Scope isoScope(_isolate); +// +// v8::HandleScope scope(_isolate); +// v8::EscapableHandleScope escape(_isolate); +// v8::Local<v8::Context> ctx = v8::Local<v8::Context>::New(_isolate, _context); +// v8::Context::Scope contextScope(ctx); // segfaults at newinstance without! + + v8::TryCatch tryCatch; + + v8::Local<v8::String> source = v8::String::New(expr.c_str()); + v8::Local<v8::Script> script = v8::Script::Compile(source); + + v8::Local<v8::Value> result; + if (!script.IsEmpty()) + result = script->Run(); + + if (script.IsEmpty() || result.IsEmpty()) { + // throw an exception + if (tryCatch.HasCaught() && !dontThrow) + throwExceptionEvent(tryCatch); + } + + return result; +} + +void V8DataModel::throwExceptionEvent(const v8::TryCatch& tryCatch) { + assert(tryCatch.HasCaught()); + ErrorEvent exceptionEvent; + exceptionEvent.name = "error.execution"; + exceptionEvent.eventType = Event::PLATFORM; + + std::string exceptionString(*v8::String::AsciiValue(tryCatch.Exception())); + exceptionEvent.data.compound["cause"] = Data(exceptionString, Data::VERBATIM);; + + v8::Local<v8::Message> message = tryCatch.Message(); + if (!message.IsEmpty()) { + std::string filename(*v8::String::AsciiValue(message->GetScriptResourceName())); + exceptionEvent.data.compound["filename"] = Data(filename, Data::VERBATIM); + + std::string sourceLine(*v8::String::AsciiValue(message->GetSourceLine())); + size_t startpos = sourceLine.find_first_not_of(" \t"); + if(std::string::npos != startpos) // remove leading white space + sourceLine = sourceLine.substr(startpos); + + exceptionEvent.data.compound["sourceline"] = Data(sourceLine, Data::VERBATIM); + + std::stringstream ssLineNumber; + int lineNumber = message->GetLineNumber(); + ssLineNumber << lineNumber; + exceptionEvent.data.compound["linenumber"] = Data(ssLineNumber.str(), Data::INTERPRETED); + + int startColumn = message->GetStartColumn(); + int endColumn = message->GetEndColumn(); + std::stringstream ssUnderline; + for (int i = 0; i < startColumn; i++) + ssUnderline << " "; + for (int i = startColumn; i < endColumn; i++) + ssUnderline << "^"; + exceptionEvent.data.compound["sourcemark"] = Data(ssUnderline.str(), Data::VERBATIM); + + std::string stackTrace(*v8::String::AsciiValue(tryCatch.StackTrace())); + exceptionEvent.data.compound["stacktrace"] = Data(stackTrace, Data::VERBATIM); + + } + +// _interpreter->receiveInternal(exceptionEvent); + throw(exceptionEvent); +} + +} diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/032317/V8DataModel.h b/src/uscxml/plugins/datamodel/ecmascript/v8/032317/V8DataModel.h new file mode 100644 index 0000000..bee59bf --- /dev/null +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/032317/V8DataModel.h @@ -0,0 +1,127 @@ +/** + * @file + * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef V8DATAMODEL_H_KN8TWG0V +#define V8DATAMODEL_H_KN8TWG0V + +#include "uscxml/config.h" +#include "uscxml/plugins/DataModelImpl.h" + +#include <list> +#include <set> +#include <v8.h> + +#ifdef BUILD_AS_PLUGINS +#include "uscxml/plugins/Plugins.h" +#endif + +namespace uscxml { +class Event; +class Data; +} + +namespace uscxml { + +/** + * @ingroup datamodel + * ECMAScript data-model via Google's V8. + */ + +class V8DataModel : public DataModelImpl { +public: + V8DataModel(); + virtual ~V8DataModel(); + virtual std::shared_ptr<DataModelImpl> create(DataModelCallbacks* callbacks); + + virtual void addExtension(DataModelExtension* ext); + + virtual std::list<std::string> getNames() { + std::list<std::string> names; + names.push_back("ecmascript"); + return names; + } + + virtual bool isValidSyntax(const std::string& expr); + virtual bool isLegalDataValue(const std::string& expr); + + virtual void setEvent(const Event& event); + + // foreach + virtual uint32_t getLength(const std::string& expr); + virtual void setForeach(const std::string& item, + const std::string& array, + const std::string& index, + uint32_t iteration); + + virtual bool evalAsBool(const std::string& expr); + virtual Data evalAsData(const std::string& expr); + virtual Data getAsData(const std::string& content); + + virtual bool isDeclared(const std::string& expr); + + virtual void assign(const std::string& location, + const Data& data, + const std::map<std::string, std::string>& attr = std::map<std::string, std::string>()); + virtual void init(const std::string& location, + const Data& data, + const std::map<std::string, std::string>& attr = std::map<std::string, std::string>()); + +protected: + virtual void setup(); + + static void jsExtension(const v8::FunctionCallbackInfo<v8::Value>& info); + static void jsIn(const v8::FunctionCallbackInfo<v8::Value>& info); + static void jsPrint(const v8::FunctionCallbackInfo<v8::Value>& info); + + //v8::Local<v8::Object> _event; // Persistent events leak .. + v8::Persistent<v8::Context> _context; + static v8::Isolate* _isolate; + + v8::Persistent<v8::Object> _ioProcessors; + v8::Persistent<v8::Object> _invokers; + + static void getIOProcessors(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info); + static void getInvokers(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info); + static void getAttribute(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info); + static void setWithException(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<void>& info); + + v8::Local<v8::Value> evalAsValue(const std::string& expr, bool dontThrow = false); + v8::Local<v8::Value> getDataAsValue(const Data& data); + Data getValueAsData(const v8::Local<v8::Value>& value); + v8::Local<v8::Value> getNodeAsValue(const XERCESC_NS::DOMNode* node); + void throwExceptionEvent(const v8::TryCatch& tryCatch); + + std::set<DataModelExtension*> _extensions; + +private: + Data getValueAsData(const v8::Local<v8::Value>& value, std::set<v8::Value*>& alreadySeen); + + static std::mutex _initMutex; + +}; + +#ifdef BUILD_AS_PLUGINS +PLUMA_INHERIT_PROVIDER(V8DataModel, DataModelImpl); +#endif + +} + +#endif /* end of include guard: V8DATAMODEL_H_KN8TWG0V */ |