//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler //////////////////////////////////////////////////////////////////////////////// #include "v8-wrapshapedjson.h" #include "Basics/conversions.h" #include "Basics/Logger.h" #include "Utils/transactions.h" #include "Utils/V8TransactionContext.h" #include "V8/v8-conv.h" #include "V8Server/v8-shape-conv.h" #include "V8Server/v8-vocbaseprivate.h" #include "VocBase/KeyGenerator.h" using namespace arangodb; using namespace arangodb::basics; using namespace arangodb::rest; //////////////////////////////////////////////////////////////////////////////// /// @brief wrapped class for TRI_shaped_json_t /// /// Layout: /// - SLOT_CLASS_TYPE /// - SLOT_CLASS /// - SLOT_DITCH //////////////////////////////////////////////////////////////////////////////// static int32_t const WRP_SHAPED_JSON_TYPE = 4; //////////////////////////////////////////////////////////////////////////////// /// @brief slot for a "ditch" //////////////////////////////////////////////////////////////////////////////// static int const SLOT_DITCH = 2; //////////////////////////////////////////////////////////////////////////////// /// @brief get basic attributes (_id, _key, _rev, _from, _to) for a JavaScript /// document object //////////////////////////////////////////////////////////////////////////////// static v8::Handle SetBasicDocumentAttributesJs( v8::Isolate* isolate, CollectionNameResolver const* resolver, TRI_v8_global_t* v8g, TRI_voc_cid_t cid, TRI_df_marker_t const* marker) { v8::EscapableHandleScope scope(isolate); TRI_ASSERT(marker != nullptr); v8::Handle result = v8::Object::New(isolate); // buffer that we'll use for generating _id, _key, _rev, _from and _to values // using a single buffer will avoid several memory allocations char buffer[TRI_COL_NAME_LENGTH + TRI_VOC_KEY_MAX_LENGTH + 2]; // _id size_t len = resolver->getCollectionName(buffer, cid); char const* docKey = TRI_EXTRACT_MARKER_KEY(marker); TRI_ASSERT(docKey != nullptr); size_t keyLength = strlen(docKey); buffer[len] = '/'; memcpy(buffer + len + 1, docKey, keyLength); TRI_GET_GLOBAL_STRING(_IdKey); result->ForceSet(_IdKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); // _key TRI_GET_GLOBAL_STRING(_KeyKey); result->ForceSet(_KeyKey, TRI_V8_PAIR_STRING(buffer + len + 1, (int)keyLength)); // _rev TRI_voc_rid_t rid = TRI_EXTRACT_MARKER_RID(marker); TRI_ASSERT(rid > 0); len = TRI_StringUInt64InPlace((uint64_t)rid, (char*)&buffer); TRI_GET_GLOBAL_STRING(_RevKey); result->ForceSet(_RevKey, TRI_V8_PAIR_STRING((char const*)buffer, (int)len)); TRI_df_marker_type_t type = marker->_type; char const* base = reinterpret_cast(marker); if (type == TRI_DOC_MARKER_KEY_EDGE) { TRI_doc_edge_key_marker_t const* m = reinterpret_cast(marker); // _from len = resolver->getCollectionNameCluster(buffer, m->_fromCid); keyLength = strlen(base + m->_offsetFromKey); buffer[len] = '/'; memcpy(buffer + len + 1, base + m->_offsetFromKey, keyLength); TRI_GET_GLOBAL_STRING(_FromKey); result->ForceSet(_FromKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); // _to if (m->_fromCid != m->_toCid) { // only lookup collection name if we haven't done it yet len = resolver->getCollectionNameCluster(buffer, m->_toCid); } keyLength = strlen(base + m->_offsetToKey); buffer[len] = '/'; memcpy(buffer + len + 1, base + m->_offsetToKey, keyLength); TRI_GET_GLOBAL_STRING(_ToKey); result->ForceSet(_ToKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); } else if (type == TRI_WAL_MARKER_EDGE) { arangodb::wal::edge_marker_t const* m = reinterpret_cast(marker); // _from len = resolver->getCollectionNameCluster(buffer, m->_fromCid); keyLength = strlen(base + m->_offsetFromKey); buffer[len] = '/'; memcpy(buffer + len + 1, base + m->_offsetFromKey, keyLength); TRI_GET_GLOBAL_STRING(_FromKey); result->ForceSet(_FromKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); // _to if (m->_fromCid != m->_toCid) { // only lookup collection name if we haven't done it yet len = resolver->getCollectionNameCluster(buffer, m->_toCid); } keyLength = strlen(base + m->_offsetToKey); buffer[len] = '/'; memcpy(buffer + len + 1, base + m->_offsetToKey, keyLength); TRI_GET_GLOBAL_STRING(_ToKey); result->ForceSet(_ToKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); } return scope.Escape(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief add basic attributes (_id, _key, _rev, _from, _to) to a ShapedJson /// object //////////////////////////////////////////////////////////////////////////////// static v8::Handle SetBasicDocumentAttributesShaped( v8::Isolate* isolate, CollectionNameResolver const* resolver, TRI_v8_global_t* v8g, TRI_voc_cid_t cid, TRI_df_marker_t const* marker, v8::Handle& result) { v8::EscapableHandleScope scope(isolate); TRI_ASSERT(marker != nullptr); // buffer that we'll use for generating _id, _key, _rev, _from and _to values // using a single buffer will avoid several memory allocations char buffer[TRI_COL_NAME_LENGTH + TRI_VOC_KEY_MAX_LENGTH + 2]; // _id size_t len = resolver->getCollectionName(buffer, cid); char const* docKey = TRI_EXTRACT_MARKER_KEY(marker); TRI_ASSERT(docKey != nullptr); size_t keyLength = strlen(docKey); buffer[len] = '/'; memcpy(buffer + len + 1, docKey, keyLength); TRI_GET_GLOBAL_STRING(_IdKey); result->ForceSet(_IdKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); TRI_df_marker_type_t type = marker->_type; char const* base = reinterpret_cast(marker); if (type == TRI_DOC_MARKER_KEY_EDGE) { TRI_doc_edge_key_marker_t const* m = reinterpret_cast(marker); // _from len = resolver->getCollectionNameCluster(buffer, m->_fromCid); keyLength = strlen(base + m->_offsetFromKey); buffer[len] = '/'; memcpy(buffer + len + 1, base + m->_offsetFromKey, keyLength); TRI_GET_GLOBAL_STRING(_FromKey); result->ForceSet(_FromKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); // _to if (m->_fromCid != m->_toCid) { // only lookup collection name if we haven't done it yet len = resolver->getCollectionNameCluster(buffer, m->_toCid); } keyLength = strlen(base + m->_offsetToKey); buffer[len] = '/'; memcpy(buffer + len + 1, base + m->_offsetToKey, keyLength); TRI_GET_GLOBAL_STRING(_ToKey); result->ForceSet(_ToKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); } else if (type == TRI_WAL_MARKER_EDGE) { arangodb::wal::edge_marker_t const* m = reinterpret_cast(marker); // _from len = resolver->getCollectionNameCluster(buffer, m->_fromCid); keyLength = strlen(base + m->_offsetFromKey); buffer[len] = '/'; memcpy(buffer + len + 1, base + m->_offsetFromKey, keyLength); TRI_GET_GLOBAL_STRING(_FromKey); result->ForceSet(_FromKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); // _to if (m->_fromCid != m->_toCid) { // only lookup collection name if we haven't done it yet len = resolver->getCollectionNameCluster(buffer, m->_toCid); } keyLength = strlen(base + m->_offsetToKey); buffer[len] = '/'; memcpy(buffer + len + 1, base + m->_offsetToKey, keyLength); TRI_GET_GLOBAL_STRING(_ToKey); result->ForceSet(_ToKey, TRI_V8_PAIR_STRING(buffer, (int)(len + keyLength + 1))); } return scope.Escape(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief weak reference callback for a ditch //////////////////////////////////////////////////////////////////////////////// static void WeakDocumentDitchCallback(const v8::WeakCallbackData< v8::External, v8::Persistent>& data) { auto isolate = data.GetIsolate(); auto persistent = data.GetParameter(); auto myDitch = v8::Local::New(isolate, *persistent); auto ditch = static_cast(myDitch->Value()); TRI_ASSERT(ditch != nullptr); TRI_GET_GLOBALS(); v8g->decreaseActiveExternals(); LOG(TRACE) << "weak-callback for document ditch called"; // find the persistent handle v8g->JSDitches[ditch].Reset(); v8g->JSDitches.erase(ditch); // get the vocbase pointer from the ditch TRI_vocbase_t* vocbase = ditch->collection()->_vocbase; ditch->ditches()->freeDocumentDitch(ditch, false /* fromTransaction */); // we don't need the ditch anymore, maybe a transaction is still using it if (vocbase != nullptr) { // decrease the reference-counter for the database TRI_ReleaseVocBase(vocbase); } } //////////////////////////////////////////////////////////////////////////////// /// @brief wraps a TRI_shaped_json_t //////////////////////////////////////////////////////////////////////////////// v8::Handle TRI_WrapShapedJson( v8::Isolate* isolate, arangodb::CollectionNameResolver const* resolver, arangodb::DocumentDitch* ditch, TRI_voc_cid_t cid, TRI_document_collection_t* collection, void const* data) { v8::EscapableHandleScope scope(isolate); TRI_df_marker_t const* marker = static_cast(data); TRI_ASSERT(marker != nullptr); TRI_ASSERT(ditch != nullptr); TRI_ASSERT(collection != nullptr); TRI_GET_GLOBALS(); bool const doCopy = TRI_IsWalDataMarkerDatafile(marker); if (doCopy) { // we'll create a full copy of the document auto shaper = collection->getShaper(); // PROTECTED by trx from above TRI_shaped_json_t json; TRI_EXTRACT_SHAPED_JSON_MARKER(json, marker); // PROTECTED by trx from above TRI_shape_t const* shape = shaper->lookupShapeId(json._sid); if (shape == nullptr) { LOG(WARN) << "cannot find shape #" << json._sid; return scope.Escape(v8::Object::New(isolate)); } v8::Handle result = SetBasicDocumentAttributesJs(isolate, resolver, v8g, cid, marker); return scope.Escape(TRI_JsonShapeData( isolate, result, shaper, shape, json._data.data, json._data.length)); } // we'll create a document stub, with a pointer into the datafile // create the new handle to return, and set its template type TRI_GET_GLOBAL(ShapedJsonTempl, v8::ObjectTemplate); v8::Handle result = ShapedJsonTempl->NewInstance(); if (result.IsEmpty()) { // error return scope.Escape(result); } // point the 0 index Field to the c++ pointer for unwrapping later result->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(isolate, WRP_SHAPED_JSON_TYPE)); result->SetInternalField( SLOT_CLASS, v8::External::New(isolate, const_cast(static_cast(marker)))); auto it = v8g->JSDitches.find(ditch); if (it == v8g->JSDitches.end()) { // tell everyone else that this ditch is used by an external ditch->setUsedByExternal(); // increase the reference-counter for the database TRI_ASSERT(ditch->collection() != nullptr); TRI_UseVocBase(ditch->collection()->_vocbase); auto externalDitch = v8::External::New(isolate, ditch); v8::Persistent& per(v8g->JSDitches[ditch]); per.Reset(isolate, externalDitch); result->SetInternalField(SLOT_DITCH, externalDitch); per.SetWeak(&per, WeakDocumentDitchCallback); v8g->increaseActiveExternals(); } else { auto myDitch = v8::Local::New(isolate, it->second); result->SetInternalField(SLOT_DITCH, myDitch); } return scope.Escape(SetBasicDocumentAttributesShaped( isolate, resolver, v8g, cid, marker, result)); } //////////////////////////////////////////////////////////////////////////////// /// @brief selects the keys from the shaped json //////////////////////////////////////////////////////////////////////////////// static void KeysOfShapedJson(const v8::PropertyCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); // sanity check v8::Handle self = args.Holder(); if (self->InternalFieldCount() <= SLOT_DITCH) { TRI_V8_RETURN(v8::Array::New(isolate)); } // get shaped json void* marker = TRI_UnwrapClass(self, WRP_SHAPED_JSON_TYPE); if (marker == nullptr) { TRI_V8_RETURN(v8::Array::New(isolate)); } auto ditch = static_cast( v8::Handle::Cast(self->GetInternalField(SLOT_DITCH)) ->Value()); TRI_document_collection_t* collection = ditch->collection(); // check for object shape auto shaper = collection->getShaper(); // PROTECTED by BARRIER, checked by RUNTIME TRI_shape_sid_t sid; TRI_EXTRACT_SHAPE_IDENTIFIER_MARKER(sid, marker); TRI_shape_aid_t const* aids; TRI_shape_size_t n; TRI_shape_t const* shape = shaper->lookupShapeId(sid); if (shape == nullptr || shape->_type != TRI_SHAPE_ARRAY) { n = 0; aids = nullptr; LOG(WARN) << "cannot find shape #" << sid; } else { // shape is an array TRI_array_shape_t const* s = (TRI_array_shape_t const*)shape; // number of entries n = s->_fixedEntries + s->_variableEntries; // calculate position of attribute ids char const* qtr = (char const*)shape; qtr += sizeof(TRI_array_shape_t); qtr += n * sizeof(TRI_shape_sid_t); aids = (TRI_shape_aid_t const*)qtr; } TRI_df_marker_type_t type = static_cast(marker)->_type; bool const isEdge = (type == TRI_DOC_MARKER_KEY_EDGE || type == TRI_WAL_MARKER_EDGE); v8::Handle result = v8::Array::New(isolate, (int)n + 3 + (isEdge ? 2 : 0)); uint32_t count = 0; TRI_GET_GLOBALS(); TRI_GET_GLOBAL_STRING(_IdKey); TRI_GET_GLOBAL_STRING(_RevKey); TRI_GET_GLOBAL_STRING(_KeyKey); result->Set(count++, _IdKey); result->Set(count++, _RevKey); result->Set(count++, _KeyKey); if (isEdge) { TRI_GET_GLOBAL_STRING(_FromKey); TRI_GET_GLOBAL_STRING(_ToKey); result->Set(count++, _FromKey); result->Set(count++, _ToKey); } for (TRI_shape_size_t i = 0; i < n; ++i, ++aids) { char const* att = shaper->lookupAttributeId(*aids); if (att != nullptr) { result->Set(count++, TRI_V8_STRING(att)); } } TRI_V8_RETURN(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief copy all shaped json attributes into the object so we have regular /// JavaScript attributes that can be modified //////////////////////////////////////////////////////////////////////////////// static void CopyAttributes(v8::Isolate* isolate, v8::Handle self, void* marker, char const* excludeAttribute = nullptr) { auto ditch = static_cast( v8::Handle::Cast(self->GetInternalField(SLOT_DITCH)) ->Value()); TRI_document_collection_t* collection = ditch->collection(); // copy _key and _rev // note: _id, _from and _to do not need to be copied because they are // already present in initial ShapedJson objects as real attributes // _key TRI_GET_GLOBALS(); char buffer[TRI_VOC_KEY_MAX_LENGTH + 1]; char const* docKey = TRI_EXTRACT_MARKER_KEY(static_cast(marker)); TRI_ASSERT(docKey != nullptr); size_t keyLength = strlen(docKey); memcpy(buffer, docKey, keyLength); if (excludeAttribute == nullptr || strcmp(excludeAttribute, TRI_VOC_ATTRIBUTE_KEY) != 0) { TRI_GET_GLOBAL_STRING(_KeyKey); self->ForceSet(_KeyKey, TRI_V8_PAIR_STRING(buffer, (int)keyLength)); } // _rev TRI_voc_rid_t rid = TRI_EXTRACT_MARKER_RID(static_cast(marker)); TRI_ASSERT(rid > 0); size_t len = TRI_StringUInt64InPlace((uint64_t)rid, (char*)&buffer); if (excludeAttribute == nullptr || strcmp(excludeAttribute, TRI_VOC_ATTRIBUTE_REV) != 0) { TRI_GET_GLOBAL_STRING(_RevKey); self->ForceSet(_RevKey, TRI_V8_PAIR_STRING((char const*)buffer, (int)len)); } // finally insert the dynamic attributes from the shaped json // check for object shape auto shaper = collection->getShaper(); // PROTECTED by BARRIER, checked by RUNTIME TRI_shape_sid_t sid; TRI_EXTRACT_SHAPE_IDENTIFIER_MARKER(sid, marker); TRI_shape_t const* shape = shaper->lookupShapeId(sid); if (shape == nullptr || shape->_type != TRI_SHAPE_ARRAY) { LOG(WARN) << "cannot find shape #" << sid; return; } TRI_array_shape_t const* s; TRI_shape_aid_t const* aids; char const* qtr; // shape is an array s = (TRI_array_shape_t const*)shape; // number of entries TRI_shape_size_t const n = s->_fixedEntries + s->_variableEntries; // calculate position of attribute ids qtr = (char const*)shape; qtr += sizeof(TRI_array_shape_t); qtr += n * sizeof(TRI_shape_sid_t); aids = (TRI_shape_aid_t const*)qtr; TRI_shaped_json_t document; TRI_EXTRACT_SHAPED_JSON_MARKER(document, marker); TRI_shaped_json_t json; for (TRI_shape_size_t i = 0; i < n; ++i, ++aids) { char const* att = shaper->lookupAttributeId(*aids); if (att != nullptr && (excludeAttribute == nullptr || strcmp(att, excludeAttribute) != 0)) { TRI_shape_pid_t pid = shaper->lookupAttributePathByName(att); if (pid != 0) { bool ok = shaper->extractShapedJson(&document, 0, pid, &json, &shape); if (ok && shape != nullptr) { self->ForceSet(TRI_V8_STRING(att), TRI_JsonShapeData(isolate, shaper, shape, json._data.data, json._data.length)); } } } } } //////////////////////////////////////////////////////////////////////////////// /// @brief selects a named attribute from the shaped json //////////////////////////////////////////////////////////////////////////////// static void MapGetNamedShapedJson( v8::Local name, const v8::PropertyCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); try { v8::HandleScope scope(isolate); // sanity check v8::Handle self = args.Holder(); if (self->InternalFieldCount() <= SLOT_DITCH) { // we better not throw here... otherwise this will cause a segfault TRI_V8_RETURN(v8::Handle()); } // get shaped json void* marker = TRI_UnwrapClass(self, WRP_SHAPED_JSON_TYPE); if (marker == nullptr) { TRI_V8_RETURN(v8::Handle()); } // convert the JavaScript string to a string // we take the fast path here and don't normalize the string v8::String::Utf8Value const str(name); std::string const key(*str, (size_t)str.length()); if (key.empty()) { TRI_V8_RETURN(v8::Handle()); } if (key[0] == '_') { char buffer[TRI_VOC_KEY_MAX_LENGTH + 1]; if (key == TRI_VOC_ATTRIBUTE_KEY) { char const* docKey = TRI_EXTRACT_MARKER_KEY(static_cast(marker)); TRI_ASSERT(docKey != nullptr); size_t keyLength = strlen(docKey); memcpy(buffer, docKey, keyLength); TRI_V8_RETURN_PAIR_STRING(buffer, (int)keyLength); } else if (key == TRI_VOC_ATTRIBUTE_REV) { TRI_voc_rid_t rid = TRI_EXTRACT_MARKER_RID(static_cast(marker)); TRI_ASSERT(rid > 0); size_t len = TRI_StringUInt64InPlace((uint64_t)rid, (char*)&buffer); TRI_V8_RETURN_PAIR_STRING((char const*)buffer, (int)len); } if (key == TRI_VOC_ATTRIBUTE_ID || key == TRI_VOC_ATTRIBUTE_FROM || key == TRI_VOC_ATTRIBUTE_TO) { // strip reserved attributes TRI_V8_RETURN(v8::Handle()); } } // TODO: check whether accessing an attribute with a dot is actually // possible, // e.g. doc.a.b vs. doc["a.b"] if (strchr(key.c_str(), '.') != nullptr) { TRI_V8_RETURN(v8::Handle()); } // get the underlying collection auto ditch = static_cast( v8::Handle::Cast(self->GetInternalField(SLOT_DITCH)) ->Value()); TRI_ASSERT(ditch != nullptr); TRI_document_collection_t* collection = ditch->collection(); // get shape accessor auto shaper = collection->getShaper(); // PROTECTED by trx here TRI_shape_pid_t pid = shaper->lookupAttributePathByName(key.c_str()); if (pid == 0) { TRI_V8_RETURN(v8::Handle()); } TRI_shaped_json_t document; TRI_EXTRACT_SHAPED_JSON_MARKER(document, marker); TRI_shaped_json_t json; TRI_shape_t const* shape; bool ok = shaper->extractShapedJson(&document, 0, pid, &json, &shape); if (ok && shape != nullptr) { TRI_V8_RETURN(TRI_JsonShapeData(isolate, shaper, shape, json._data.data, json._data.length)); } // we must not throw a v8 exception here because this will cause follow up // errors TRI_V8_RETURN(v8::Handle()); } catch (...) { TRI_V8_RETURN(v8::Handle()); } } //////////////////////////////////////////////////////////////////////////////// /// @brief sets a named attribute in the shaped json /// Returns the value if the setter intercepts the request. /// Otherwise, returns an empty handle. //////////////////////////////////////////////////////////////////////////////// static void MapSetNamedShapedJson( v8::Local name, v8::Local value, const v8::PropertyCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); try { v8::HandleScope scope(isolate); // sanity check v8::Handle self = args.Holder(); if (self->InternalFieldCount() <= SLOT_DITCH) { // we better not throw here... otherwise this will cause a segfault TRI_V8_RETURN(v8::Handle()); } // get shaped json void* marker = TRI_UnwrapClass(self, WRP_SHAPED_JSON_TYPE); if (marker == nullptr) { return; } if (self->HasRealNamedProperty(name)) { // object already has the property. use the regular property setter self->ForceSet(name, value); TRI_V8_RETURN_TRUE(); } // copy all attributes from the shaped json into the object CopyAttributes(isolate, self, marker, nullptr); // remove pointer to marker, so the object becomes stand-alone self->SetInternalField(SLOT_CLASS, v8::External::New(isolate, nullptr)); // and now use the regular property setter self->ForceSet(name, value); TRI_V8_RETURN_TRUE(); } catch (...) { TRI_V8_RETURN(v8::Handle()); } } //////////////////////////////////////////////////////////////////////////////// /// @brief deletes a named attribute from the shaped json /// Returns a non-empty handle if the deleter intercepts the request. /// The return value is true if the property could be deleted and false /// otherwise. //////////////////////////////////////////////////////////////////////////////// static void MapDeleteNamedShapedJson( v8::Local name, const v8::PropertyCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); try { v8::HandleScope scope(isolate); // sanity check v8::Handle self = args.Holder(); if (self->InternalFieldCount() <= SLOT_DITCH) { // we better not throw here... otherwise this will cause a segfault return; } // get shaped json void* marker = TRI_UnwrapClass(self, WRP_SHAPED_JSON_TYPE); if (marker == nullptr) { TRI_V8_RETURN(v8::Handle()); } // remove pointer to marker, so the object becomes stand-alone self->SetInternalField(SLOT_CLASS, v8::External::New(isolate, nullptr)); // copy all attributes from the shaped json into the object // but the to-be-deleted attribute std::string&& nameString = TRI_ObjectToString(name); CopyAttributes(isolate, self, marker, nameString.c_str()); TRI_V8_RETURN(v8::Handle()); } catch (...) { return; } } //////////////////////////////////////////////////////////////////////////////// /// @brief check if a property is present //////////////////////////////////////////////////////////////////////////////// static void PropertyQueryShapedJson( v8::Local name, const v8::PropertyCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); try { v8::HandleScope scope(isolate); v8::Handle self = args.Holder(); // sanity check if (self->InternalFieldCount() <= SLOT_DITCH) { TRI_V8_RETURN(v8::Handle()); } // get shaped json void* marker = TRI_UnwrapClass(self, WRP_SHAPED_JSON_TYPE); if (marker == nullptr) { TRI_V8_RETURN(v8::Handle()); } // convert the JavaScript string to a string std::string const&& key = TRI_ObjectToString(name); if (key.empty()) { TRI_V8_RETURN(v8::Handle()); } if (key[0] == '_') { if (key == TRI_VOC_ATTRIBUTE_KEY || key == TRI_VOC_ATTRIBUTE_REV || key == TRI_VOC_ATTRIBUTE_ID || key == TRI_VOC_ATTRIBUTE_FROM || key == TRI_VOC_ATTRIBUTE_TO) { TRI_V8_RETURN( v8::Handle(v8::Integer::New(isolate, v8::None))); } } // get underlying collection auto ditch = static_cast( v8::Handle::Cast(self->GetInternalField(SLOT_DITCH)) ->Value()); TRI_document_collection_t* collection = ditch->collection(); // get shape accessor auto shaper = collection->getShaper(); // PROTECTED by BARRIER, checked by RUNTIME TRI_shape_pid_t pid = shaper->lookupAttributePathByName(key.c_str()); if (pid == 0) { TRI_V8_RETURN(v8::Handle()); } TRI_shape_sid_t sid; TRI_EXTRACT_SHAPE_IDENTIFIER_MARKER(sid, marker); if (sid == TRI_SHAPE_ILLEGAL) { // invalid shape #ifdef TRI_ENABLE_MAINTAINER_MODE LOG(WARN) << "invalid shape id '" << sid << "' found for key '" << key.c_str() << "'"; #endif TRI_V8_RETURN(v8::Handle()); } TRI_shape_access_t const* acc = shaper->findAccessor(sid, pid); // key not found if (acc == nullptr || acc->_resultSid == TRI_SHAPE_ILLEGAL) { TRI_V8_RETURN(v8::Handle()); } TRI_V8_RETURN(v8::Handle(v8::Integer::New(isolate, v8::None))); } catch (...) { TRI_V8_RETURN(v8::Handle()); } } //////////////////////////////////////////////////////////////////////////////// /// @brief selects an indexed attribute from the shaped json //////////////////////////////////////////////////////////////////////////////// static void MapGetIndexedShapedJson( uint32_t idx, const v8::PropertyCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); char buffer[11]; size_t len = TRI_StringUInt32InPlace(idx, (char*)&buffer); v8::Local strVal = TRI_V8_PAIR_STRING((char*)&buffer, (int)len); MapGetNamedShapedJson(strVal, args); } //////////////////////////////////////////////////////////////////////////////// /// @brief sets an indexed attribute in the shaped json //////////////////////////////////////////////////////////////////////////////// static void MapSetIndexedShapedJson( uint32_t idx, v8::Local value, const v8::PropertyCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); char buffer[11]; size_t len = TRI_StringUInt32InPlace(idx, (char*)&buffer); v8::Local strVal = TRI_V8_PAIR_STRING((char*)&buffer, (int)len); MapSetNamedShapedJson(strVal, value, args); } //////////////////////////////////////////////////////////////////////////////// /// @brief delete an indexed attribute in the shaped json //////////////////////////////////////////////////////////////////////////////// static void MapDeleteIndexedShapedJson( uint32_t idx, const v8::PropertyCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); char buffer[11]; size_t len = TRI_StringUInt32InPlace(idx, (char*)&buffer); v8::Local strVal = TRI_V8_PAIR_STRING((char*)&buffer, (int)len); MapDeleteNamedShapedJson(strVal, args); } //////////////////////////////////////////////////////////////////////////////// /// @brief generate the TRI_shaped_json_t template //////////////////////////////////////////////////////////////////////////////// void TRI_InitV8ShapedJson(v8::Isolate* isolate, v8::Handle context, size_t threadNumber, TRI_v8_global_t* v8g) { v8::Handle rt; v8::Handle ft; ft = v8::FunctionTemplate::New(isolate); ft->SetClassName(TRI_V8_ASCII_STRING("ShapedJson")); rt = ft->InstanceTemplate(); rt->SetInternalFieldCount(3); // accessor for named properties (e.g. doc.abcdef) rt->SetNamedPropertyHandler( MapGetNamedShapedJson, // NamedPropertyGetter, MapSetNamedShapedJson, // NamedPropertySetter setter PropertyQueryShapedJson, // NamedPropertyQuery, MapDeleteNamedShapedJson, // NamedPropertyDeleter deleter, KeysOfShapedJson // NamedPropertyEnumerator, // Handle data = Handle()); ); // accessor for indexed properties (e.g. doc[1]) rt->SetIndexedPropertyHandler( MapGetIndexedShapedJson, // IndexedPropertyGetter, MapSetIndexedShapedJson, // IndexedPropertySetter, nullptr, // IndexedPropertyQuery, MapDeleteIndexedShapedJson, // IndexedPropertyDeleter, nullptr // IndexedPropertyEnumerator, // Handle data = Handle()); ); v8g->ShapedJsonTempl.Reset(isolate, rt); TRI_AddGlobalFunctionVocbase( isolate, context, TRI_V8_ASCII_STRING("ShapedJson"), ft->GetFunction()); }