diff --git a/CHANGELOG b/CHANGELOG index 559cab3034..13902a03da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ devel ----- +* added a memory expection of V8 memory gets to low + +* fixed epoch computation in hybrid logical clock + * fixed thread affinity * replaced require("internal").db by require("@arangodb").db diff --git a/arangod/Aql/AqlValue.cpp b/arangod/Aql/AqlValue.cpp index 4cbe00f497..92fd03cf2d 100644 --- a/arangod/Aql/AqlValue.cpp +++ b/arangod/Aql/AqlValue.cpp @@ -41,17 +41,17 @@ using namespace arangodb::aql; uint64_t AqlValue::hash(arangodb::AqlTransaction* trx, uint64_t seed) const { switch (type()) { case VPACK_SLICE_POINTER: - case VPACK_INLINE: + case VPACK_INLINE: case VPACK_MANAGED: { - // we must use the slow hash function here, because a value may have + // we must use the slow hash function here, because a value may have // different representations in case its an array/object/number return slice().normalizedHash(seed); } case DOCVEC: - case RANGE: { + case RANGE: { VPackBuilder builder; toVelocyPack(trx, builder, false); - // we must use the slow hash function here, because a value may have + // we must use the slow hash function here, because a value may have // different representations in case its an array/object/number return builder.slice().normalizedHash(seed); } @@ -66,7 +66,7 @@ bool AqlValue::isNone() const { if (t == DOCVEC || t == RANGE) { return false; } - + return slice().isNone(); } @@ -76,7 +76,7 @@ bool AqlValue::isNull(bool emptyIsNull) const { if (t == DOCVEC || t == RANGE) { return false; } - + VPackSlice s(slice()); return (s.isNull() || (emptyIsNull && s.isNone())); } @@ -126,7 +126,7 @@ bool AqlValue::isArray() const { } return slice().isArray(); } - + /// @brief get the (array) length (note: this treats ranges as arrays, too!) size_t AqlValue::length() const { switch (type()) { @@ -139,23 +139,22 @@ size_t AqlValue::length() const { return docvecSize(); } case RANGE: { - return range()->size(); + return range()->size(); } } TRI_ASSERT(false); return 0; } - -/// @brief get the (array) element at position -AqlValue AqlValue::at(arangodb::AqlTransaction* trx, - int64_t position, bool& mustDestroy, - bool doCopy) const { + +/// @brief get the (array) element at position +AqlValue AqlValue::at(arangodb::AqlTransaction* trx, int64_t position, + bool& mustDestroy, bool doCopy) const { mustDestroy = false; switch (type()) { case VPACK_SLICE_POINTER: - doCopy = false; + doCopy = false; case VPACK_INLINE: - // fall-through intentional + // fall-through intentional case VPACK_MANAGED: { VPackSlice s(slice()); if (s.isArray()) { @@ -190,7 +189,9 @@ AqlValue AqlValue::at(arangodb::AqlTransaction* trx, // found the correct vector if (doCopy) { mustDestroy = true; - return it->getValueReference(static_cast(position - total), 0).clone(); + return it + ->getValueReference(static_cast(position - total), 0) + .clone(); } return it->getValue(static_cast(position - total), 0); } @@ -210,7 +211,8 @@ AqlValue AqlValue::at(arangodb::AqlTransaction* trx, if (position >= 0 && position < static_cast(n)) { // only look up the value if it is within array bounds TransactionBuilderLeaser builder(trx); - builder->add(VPackValue(_data.range->at(static_cast(position)))); + builder->add( + VPackValue(_data.range->at(static_cast(position)))); mustDestroy = true; return AqlValue(builder->slice()); } @@ -229,9 +231,9 @@ AqlValue AqlValue::getKeyAttribute(arangodb::AqlTransaction* trx, mustDestroy = false; switch (type()) { case VPACK_SLICE_POINTER: - doCopy = false; + doCopy = false; case VPACK_INLINE: - // fall-through intentional + // fall-through intentional case VPACK_MANAGED: { VPackSlice s(slice()); if (s.isObject()) { @@ -248,7 +250,7 @@ AqlValue AqlValue::getKeyAttribute(arangodb::AqlTransaction* trx, // fall-through intentional break; } - case DOCVEC: + case DOCVEC: case RANGE: { // will return null break; @@ -265,14 +267,14 @@ AqlValue AqlValue::getIdAttribute(arangodb::AqlTransaction* trx, mustDestroy = false; switch (type()) { case VPACK_SLICE_POINTER: - doCopy = false; + doCopy = false; case VPACK_INLINE: - // fall-through intentional + // fall-through intentional case VPACK_MANAGED: { VPackSlice s(slice()); if (s.isObject()) { VPackSlice found = Transaction::extractIdFromDocument(s); - if (found.isCustom()) { + if (found.isCustom()) { // _id as a custom type needs special treatment mustDestroy = true; return AqlValue(trx->extractIdString(trx->resolver(), found, s)); @@ -289,7 +291,7 @@ AqlValue AqlValue::getIdAttribute(arangodb::AqlTransaction* trx, // fall-through intentional break; } - case DOCVEC: + case DOCVEC: case RANGE: { // will return null break; @@ -306,9 +308,9 @@ AqlValue AqlValue::getFromAttribute(arangodb::AqlTransaction* trx, mustDestroy = false; switch (type()) { case VPACK_SLICE_POINTER: - doCopy = false; + doCopy = false; case VPACK_INLINE: - // fall-through intentional + // fall-through intentional case VPACK_MANAGED: { VPackSlice s(slice()); if (s.isObject()) { @@ -325,7 +327,7 @@ AqlValue AqlValue::getFromAttribute(arangodb::AqlTransaction* trx, // fall-through intentional break; } - case DOCVEC: + case DOCVEC: case RANGE: { // will return null break; @@ -342,9 +344,9 @@ AqlValue AqlValue::getToAttribute(arangodb::AqlTransaction* trx, mustDestroy = false; switch (type()) { case VPACK_SLICE_POINTER: - doCopy = false; + doCopy = false; case VPACK_INLINE: - // fall-through intentional + // fall-through intentional case VPACK_MANAGED: { VPackSlice s(slice()); if (s.isObject()) { @@ -361,7 +363,7 @@ AqlValue AqlValue::getToAttribute(arangodb::AqlTransaction* trx, // fall-through intentional break; } - case DOCVEC: + case DOCVEC: case RANGE: { // will return null break; @@ -371,22 +373,21 @@ AqlValue AqlValue::getToAttribute(arangodb::AqlTransaction* trx, // default is to return null return AqlValue(arangodb::basics::VelocyPackHelper::NullValue()); } - + /// @brief get the (object) element by name -AqlValue AqlValue::get(arangodb::AqlTransaction* trx, - std::string const& name, bool& mustDestroy, - bool doCopy) const { +AqlValue AqlValue::get(arangodb::AqlTransaction* trx, std::string const& name, + bool& mustDestroy, bool doCopy) const { mustDestroy = false; switch (type()) { case VPACK_SLICE_POINTER: doCopy = false; case VPACK_INLINE: - // fall-through intentional + // fall-through intentional case VPACK_MANAGED: { VPackSlice s(slice()); if (s.isObject()) { VPackSlice found(s.get(name)); - if (found.isCustom()) { + if (found.isCustom()) { // _id needs special treatment mustDestroy = true; return AqlValue(trx->extractIdString(s)); @@ -403,7 +404,7 @@ AqlValue AqlValue::get(arangodb::AqlTransaction* trx, // fall-through intentional break; } - case DOCVEC: + case DOCVEC: case RANGE: { // will return null break; @@ -416,8 +417,8 @@ AqlValue AqlValue::get(arangodb::AqlTransaction* trx, /// @brief get the (object) element(s) by name AqlValue AqlValue::get(arangodb::AqlTransaction* trx, - std::vector const& names, - bool& mustDestroy, bool doCopy) const { + std::vector const& names, bool& mustDestroy, + bool doCopy) const { mustDestroy = false; if (names.empty()) { return AqlValue(arangodb::basics::VelocyPackHelper::NullValue()); @@ -425,10 +426,10 @@ AqlValue AqlValue::get(arangodb::AqlTransaction* trx, switch (type()) { case VPACK_SLICE_POINTER: - doCopy = false; - // fall-through intentional + doCopy = false; + // fall-through intentional case VPACK_INLINE: - // fall-through intentional + // fall-through intentional case VPACK_MANAGED: { VPackSlice s(slice()); if (s.isObject()) { @@ -444,7 +445,7 @@ AqlValue AqlValue::get(arangodb::AqlTransaction* trx, if (s.isExternal()) { s = s.resolveExternal(); } - + if (s.isNone()) { // not found return AqlValue(arangodb::basics::VelocyPackHelper::NullValue()); @@ -474,7 +475,7 @@ AqlValue AqlValue::get(arangodb::AqlTransaction* trx, // fall-through intentional break; } - case DOCVEC: + case DOCVEC: case RANGE: { // will return null break; @@ -495,7 +496,7 @@ bool AqlValue::hasKey(arangodb::AqlTransaction* trx, VPackSlice s(slice()); return (s.isObject() && s.hasKey(name)); } - case DOCVEC: + case DOCVEC: case RANGE: { break; } @@ -507,7 +508,7 @@ bool AqlValue::hasKey(arangodb::AqlTransaction* trx, /// @brief get the numeric value of an AqlValue double AqlValue::toDouble(arangodb::AqlTransaction* trx) const { - bool failed; // will be ignored + bool failed; // will be ignored return toDouble(trx, failed); } @@ -549,14 +550,13 @@ double AqlValue::toDouble(arangodb::AqlTransaction* trx, bool& failed) const { // conversion failed break; } - } - else if (s.isArray()) { + } else if (s.isArray()) { auto length = s.length(); if (length == 0) { return 0.0; } if (length == 1) { - bool mustDestroy; // we can ignore destruction here + bool mustDestroy; // we can ignore destruction here return at(trx, 0, mustDestroy, false).toDouble(trx, failed); } } @@ -566,7 +566,7 @@ double AqlValue::toDouble(arangodb::AqlTransaction* trx, bool& failed) const { case DOCVEC: case RANGE: { if (length() == 1) { - bool mustDestroy; // we can ignore destruction here + bool mustDestroy; // we can ignore destruction here return at(trx, 0, mustDestroy, false).toDouble(trx, failed); } // will return 0 @@ -601,8 +601,7 @@ int64_t AqlValue::toInt64(arangodb::AqlTransaction* trx) const { } // conversion failed } - } - else if (s.isArray()) { + } else if (s.isArray()) { auto length = s.length(); if (length == 0) { return 0; @@ -639,13 +638,13 @@ bool AqlValue::toBoolean() const { VPackSlice s(slice()); if (s.isBoolean()) { return s.getBoolean(); - } + } if (s.isNumber()) { return (s.getNumber() != 0.0); - } + } if (s.isString()) { return (s.getStringLength() > 0); - } + } if (s.isArray() || s.isObject() || s.isCustom()) { // custom _id type is also true return true; @@ -653,7 +652,7 @@ bool AqlValue::toBoolean() const { // all other cases, including Null and None return false; } - case DOCVEC: + case DOCVEC: case RANGE: { return true; } @@ -723,9 +722,8 @@ v8::Handle AqlValue::toV8Partial( } /// @brief construct a V8 value as input for the expression execution in V8 -v8::Handle AqlValue::toV8( - v8::Isolate* isolate, arangodb::AqlTransaction* trx) const { - +v8::Handle AqlValue::toV8(v8::Isolate* isolate, + arangodb::AqlTransaction* trx) const { switch (type()) { case VPACK_SLICE_POINTER: case VPACK_INLINE: @@ -744,6 +742,10 @@ v8::Handle AqlValue::toV8( size_t const n = it->size(); for (size_t i = 0; i < n; ++i) { result->Set(j++, it->getValueReference(i, 0).toV8(isolate, trx)); + + if (V8PlatformFeature::isOutOfMemory(isolate)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } } } return result; @@ -755,8 +757,15 @@ v8::Handle AqlValue::toV8( for (uint32_t i = 0; i < n; ++i) { // is it safe to use a double here (precision loss)? - result->Set(i, v8::Number::New(isolate, - static_cast(_data.range->at(static_cast(i))))); + result->Set( + i, v8::Number::New(isolate, static_cast(_data.range->at( + static_cast(i))))); + + if (i % 1000 == 0) { + if (V8PlatformFeature::isOutOfMemory(isolate)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } + } } return result; } @@ -767,7 +776,7 @@ v8::Handle AqlValue::toV8( } /// @brief materializes a value into the builder -void AqlValue::toVelocyPack(AqlTransaction* trx, +void AqlValue::toVelocyPack(AqlTransaction* trx, arangodb::velocypack::Builder& builder, bool resolveExternals) const { switch (type()) { @@ -775,8 +784,8 @@ void AqlValue::toVelocyPack(AqlTransaction* trx, if (!resolveExternals && isMasterPointer()) { builder.addExternal(_data.pointer); break; - } // fallthrough intentional - case VPACK_INLINE: + } // fallthrough intentional + case VPACK_INLINE: case VPACK_MANAGED: { if (resolveExternals) { arangodb::basics::VelocyPackHelper::SanitizeExternals(slice(), builder); @@ -790,7 +799,8 @@ void AqlValue::toVelocyPack(AqlTransaction* trx, for (auto const& it : *_data.docvec) { size_t const n = it->size(); for (size_t i = 0; i < n; ++i) { - it->getValueReference(i, 0).toVelocyPack(trx, builder, resolveExternals); + it->getValueReference(i, 0).toVelocyPack(trx, builder, + resolveExternals); } } builder.close(); @@ -809,19 +819,21 @@ void AqlValue::toVelocyPack(AqlTransaction* trx, } /// @brief materializes a value into the builder -AqlValue AqlValue::materialize(AqlTransaction* trx, bool& hasCopied, bool resolveExternals) const { +AqlValue AqlValue::materialize(AqlTransaction* trx, bool& hasCopied, + bool resolveExternals) const { switch (type()) { case VPACK_SLICE_POINTER: - case VPACK_INLINE: + case VPACK_INLINE: case VPACK_MANAGED: { hasCopied = false; return *this; } - case DOCVEC: + case DOCVEC: case RANGE: { bool shouldDelete = true; ConditionalDeleter> deleter(shouldDelete); - std::shared_ptr> buffer(new VPackBuffer, deleter); + std::shared_ptr> buffer(new VPackBuffer, + deleter); VPackBuilder builder(buffer); toVelocyPack(trx, builder, resolveExternals); hasCopied = true; @@ -853,7 +865,8 @@ AqlValue AqlValue::clone() const { // copy buffer VPackValueLength length = _data.buffer->size(); auto buffer = new VPackBuffer(length); - buffer->append(reinterpret_cast(_data.buffer->data()), length); + buffer->append(reinterpret_cast(_data.buffer->data()), + length); return AqlValue(buffer); } case DOCVEC: { @@ -883,8 +896,8 @@ AqlValue AqlValue::clone() const { /// @brief destroy the value's internals void AqlValue::destroy() { - switch (type()) { - case VPACK_SLICE_POINTER: + switch (type()) { + case VPACK_SLICE_POINTER: case VPACK_INLINE: { // nothing to do return; @@ -905,8 +918,8 @@ void AqlValue::destroy() { break; } } - - erase(); // to prevent duplicate deletion + + erase(); // to prevent duplicate deletion } /// @brief return the slice from the value @@ -941,10 +954,10 @@ VPackSlice AqlValue::slice() const { AqlValue AqlValue::CreateFromBlocks( arangodb::AqlTransaction* trx, std::vector const& src, std::vector const& variableNames) { - bool shouldDelete = true; ConditionalDeleter> deleter(shouldDelete); - std::shared_ptr> buffer(new VPackBuffer, deleter); + std::shared_ptr> buffer(new VPackBuffer, + deleter); VPackBuilder builder(buffer); builder.openArray(); @@ -980,17 +993,18 @@ AqlValue AqlValue::CreateFromBlocks( AqlValue AqlValue::CreateFromBlocks( arangodb::AqlTransaction* trx, std::vector const& src, arangodb::aql::RegisterId expressionRegister) { - bool shouldDelete = true; ConditionalDeleter> deleter(shouldDelete); - std::shared_ptr> buffer(new VPackBuffer, deleter); + std::shared_ptr> buffer(new VPackBuffer, + deleter); VPackBuilder builder(buffer); builder.openArray(); for (auto const& current : src) { for (size_t i = 0; i < current->size(); ++i) { - current->getValueReference(i, expressionRegister).toVelocyPack(trx, builder, false); + current->getValueReference(i, expressionRegister) + .toVelocyPack(trx, builder, false); } } @@ -1000,23 +1014,24 @@ AqlValue AqlValue::CreateFromBlocks( /// @brief 3-way comparison for AqlValue objects int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left, - AqlValue const& right, - bool compareUtf8) { + AqlValue const& right, bool compareUtf8) { VPackOptions* options = trx->transactionContext()->getVPackOptions(); AqlValue::AqlValueType const leftType = left.type(); AqlValue::AqlValueType const rightType = right.type(); if (leftType != rightType) { - if (leftType == RANGE || rightType == RANGE || leftType == DOCVEC || rightType == DOCVEC) { + if (leftType == RANGE || rightType == RANGE || leftType == DOCVEC || + rightType == DOCVEC) { // range|docvec against x VPackBuilder leftBuilder; left.toVelocyPack(trx, leftBuilder, false); VPackBuilder rightBuilder; right.toVelocyPack(trx, rightBuilder, false); - - return arangodb::basics::VelocyPackHelper::compare(leftBuilder.slice(), rightBuilder.slice(), compareUtf8, options); + + return arangodb::basics::VelocyPackHelper::compare( + leftBuilder.slice(), rightBuilder.slice(), compareUtf8, options); } // fall-through to other types intentional } @@ -1027,7 +1042,8 @@ int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left, case VPACK_SLICE_POINTER: case VPACK_INLINE: case VPACK_MANAGED: { - return arangodb::basics::VelocyPackHelper::compare(left.slice(), right.slice(), compareUtf8, options); + return arangodb::basics::VelocyPackHelper::compare( + left.slice(), right.slice(), compareUtf8, options); } case DOCVEC: { // use lexicographic ordering of AqlValues regardless of block, @@ -1051,8 +1067,10 @@ int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left, size_t rrows = right._data.docvec->at(0)->size(); while (lblock < lsize && rblock < rsize) { - AqlValue const& lval = left._data.docvec->at(lblock)->getValueReference(litem, 0); - AqlValue const& rval = right._data.docvec->at(rblock)->getValueReference(ritem, 0); + AqlValue const& lval = + left._data.docvec->at(lblock)->getValueReference(litem, 0); + AqlValue const& rval = + right._data.docvec->at(rblock)->getValueReference(ritem, 0); int cmp = Compare(trx, lval, rval, compareUtf8); @@ -1101,4 +1119,3 @@ int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left, return 0; } - diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index 0ec74ef188..b232613c20 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -721,8 +721,7 @@ QueryResult Query::execute(QueryRegistry* registry) { } } -/// @brief execute an AQL query -/// may only be called with an active V8 handle scope +// execute an AQL query: may only be called with an active V8 handle scope QueryResultV8 Query::executeV8(v8::Isolate* isolate, QueryRegistry* registry) { LOG_TOPIC(DEBUG, Logger::QUERIES) << TRI_microtime() - _startTime << " " << "Query::executeV8" << " this: " << (uintptr_t) this; @@ -829,6 +828,10 @@ QueryResultV8 Query::executeV8(v8::Isolate* isolate, QueryRegistry* registry) { if (!val.isEmpty()) { result.result->Set(j++, val.toV8(isolate, _trx)); } + + if (V8PlatformFeature::isOutOfMemory(isolate)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } } } delete value; diff --git a/arangod/RestServer/ConsoleThread.cpp b/arangod/RestServer/ConsoleThread.cpp index 1b9367ab20..02693a854e 100644 --- a/arangod/RestServer/ConsoleThread.cpp +++ b/arangod/RestServer/ConsoleThread.cpp @@ -23,8 +23,8 @@ #include "ConsoleThread.h" -#include #include +#include #include "ApplicationFeatures/ApplicationServer.h" #include "Basics/MutexLocker.h" @@ -157,14 +157,20 @@ start_pretty_print(); } while (!isStopping() && !_userAborted.load()) { - if (nrCommands >= gcInterval) { + if (nrCommands >= gcInterval || + V8PlatformFeature::isOutOfMemory(isolate)) { TRI_RunGarbageCollectionV8(isolate, 0.5); nrCommands = 0; + + // needs to be reset after the garbage collection + V8PlatformFeature::resetOutOfMemory(isolate); } std::string input; bool eof; + isolate->CancelTerminateExecution(); + { MUTEX_LOCKER(mutexLocker, serverConsoleMutex); input = console.prompt("arangod> ", "arangod", eof); diff --git a/arangod/Utils/V8TransactionContext.cpp b/arangod/Utils/V8TransactionContext.cpp index f0e24766c1..3b423396cc 100644 --- a/arangod/Utils/V8TransactionContext.cpp +++ b/arangod/Utils/V8TransactionContext.cpp @@ -25,8 +25,8 @@ #include "Utils/CollectionNameResolver.h" #include "VocBase/transaction.h" -#include "V8/v8-globals.h" #include +#include "V8/v8-globals.h" using namespace arangodb; @@ -34,28 +34,31 @@ using namespace arangodb; /// @brief create the context //////////////////////////////////////////////////////////////////////////////// -V8TransactionContext::V8TransactionContext(TRI_vocbase_t* vocbase, bool embeddable) +V8TransactionContext::V8TransactionContext(TRI_vocbase_t* vocbase, + bool embeddable) : TransactionContext(vocbase), _sharedTransactionContext(static_cast( static_cast(v8::Isolate::GetCurrent()->GetData( - V8DataSlot))->_transactionContext)), + V8PlatformFeature::V8_DATA_SLOT)) + ->_transactionContext)), _mainScope(nullptr), _currentTransaction(nullptr), - _embeddable(embeddable) { -} + _embeddable(embeddable) {} ////////////////////////////////////////////////////////////////////////////// /// @brief order a custom type handler for the collection ////////////////////////////////////////////////////////////////////////////// -std::shared_ptr V8TransactionContext::orderCustomTypeHandler() { +std::shared_ptr +V8TransactionContext::orderCustomTypeHandler() { if (_customTypeHandler == nullptr) { V8TransactionContext* main = _sharedTransactionContext->_mainScope; - + if (main != nullptr && main != this && !main->isGlobal()) { _customTypeHandler = main->orderCustomTypeHandler(); } else { - _customTypeHandler.reset(TransactionContext::createCustomTypeHandler(_vocbase, getResolver())); + _customTypeHandler.reset( + TransactionContext::createCustomTypeHandler(_vocbase, getResolver())); } _options.customTypeHandler = _customTypeHandler.get(); _dumpOptions.customTypeHandler = _customTypeHandler.get(); @@ -82,7 +85,7 @@ CollectionNameResolver const* V8TransactionContext::getResolver() { _resolver = createResolver(); } } - + TRI_ASSERT(_resolver != nullptr); return _resolver; } @@ -132,7 +135,7 @@ bool V8TransactionContext::isEmbeddable() const { return _embeddable; } //////////////////////////////////////////////////////////////////////////////// void V8TransactionContext::makeGlobal() { _sharedTransactionContext = this; } - + //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the transaction context is a global one //////////////////////////////////////////////////////////////////////////////// @@ -147,19 +150,19 @@ bool V8TransactionContext::isGlobal() const { bool V8TransactionContext::IsEmbedded() { TRI_v8_global_t* v8g = static_cast( - v8::Isolate::GetCurrent()->GetData(V8DataSlot)); + v8::Isolate::GetCurrent()->GetData(V8PlatformFeature::V8_DATA_SLOT)); if (v8g->_transactionContext == nullptr) { return false; } return static_cast(v8g->_transactionContext) ->_currentTransaction != nullptr; } - + //////////////////////////////////////////////////////////////////////////////// /// @brief create a context, returned in a shared ptr //////////////////////////////////////////////////////////////////////////////// -std::shared_ptr V8TransactionContext::Create(TRI_vocbase_t* vocbase, bool embeddable) { +std::shared_ptr V8TransactionContext::Create( + TRI_vocbase_t* vocbase, bool embeddable) { return std::make_shared(vocbase, embeddable); } - diff --git a/arangod/V8Server/V8DealerFeature.cpp b/arangod/V8Server/V8DealerFeature.cpp index 4094f1cdfc..2bc5bd8167 100644 --- a/arangod/V8Server/V8DealerFeature.cpp +++ b/arangod/V8Server/V8DealerFeature.cpp @@ -568,6 +568,27 @@ void V8DealerFeature::exitContext(V8Context* context) { bool canceled = false; + if (V8PlatformFeature::isOutOfMemory(isolate)) { + static double const availableTime = 300.0; + + v8::HandleScope scope(isolate); + { + auto localContext = + v8::Local::New(isolate, context->_context); + localContext->Enter(); + + { + v8::Context::Scope contextScope(localContext); + TRI_RunGarbageCollectionV8(isolate, availableTime); + } + + // needs to be reset after the garbage collection + V8PlatformFeature::resetOutOfMemory(isolate); + + localContext->Exit(); + } + } + // update data for later garbage collection { TRI_GET_GLOBALS(); @@ -858,10 +879,7 @@ void V8DealerFeature::initializeContext(size_t i) { "V8Platform"); TRI_ASSERT(v8platform != nullptr); - v8::Isolate::CreateParams createParams; - createParams.array_buffer_allocator = v8platform->arrayBufferAllocator(); - v8::Isolate* isolate = v8::Isolate::New(createParams); - + v8::Isolate* isolate = v8platform->createIsolate(); V8Context* context = _contexts[i] = new V8Context(); TRI_ASSERT(context->_locker == nullptr); diff --git a/arangosh/Shell/V8ShellFeature.cpp b/arangosh/Shell/V8ShellFeature.cpp index 6a00d561be..57fe125eb4 100644 --- a/arangosh/Shell/V8ShellFeature.cpp +++ b/arangosh/Shell/V8ShellFeature.cpp @@ -83,7 +83,6 @@ void V8ShellFeature::collectOptions(std::shared_ptr options) { void V8ShellFeature::validateOptions( std::shared_ptr options) { - if (_startupDirectory.empty()) { LOG(FATAL) << "'--javascript.startup-directory' is empty, giving up"; FATAL_ERROR_EXIT(); @@ -94,12 +93,14 @@ void V8ShellFeature::validateOptions( } void V8ShellFeature::start() { - _console = application_features::ApplicationServer::getFeature("Console"); - auto platform = application_features::ApplicationServer::getFeature("V8Platform"); + _console = + application_features::ApplicationServer::getFeature( + "Console"); + auto platform = + application_features::ApplicationServer::getFeature( + "V8Platform"); - v8::Isolate::CreateParams createParams; - createParams.array_buffer_allocator = platform->arrayBufferAllocator(); - _isolate = v8::Isolate::New(createParams); + _isolate = platform->createIsolate(); v8::Locker locker{_isolate}; @@ -141,8 +142,8 @@ void V8ShellFeature::unprepare() { v8::Local::New(_isolate, _context); v8::Context::Scope context_scope{context}; - - // remove any objects stored in _last global value + + // remove any objects stored in _last global value context->Global()->Delete(TRI_V8_ASCII_STRING2(_isolate, "_last")); TRI_RunGarbageCollectionV8(_isolate, 2500.0); @@ -152,9 +153,9 @@ void V8ShellFeature::unprepare() { v8::Locker locker{_isolate}; v8::Isolate::Scope isolate_scope{_isolate}; - TRI_v8_global_t* v8g = - static_cast(_isolate->GetData(V8DataSlot)); - _isolate->SetData(V8DataSlot, nullptr); + TRI_v8_global_t* v8g = static_cast( + _isolate->GetData(arangodb::V8PlatformFeature::V8_DATA_SLOT)); + _isolate->SetData(arangodb::V8PlatformFeature::V8_DATA_SLOT, nullptr); delete v8g; @@ -401,9 +402,13 @@ int V8ShellFeature::runShell(std::vector const& positionals) { _console->flushLog(); // gc - if (++nrCommands >= _gcInterval) { + if (++nrCommands >= _gcInterval || + V8PlatformFeature::isOutOfMemory(_isolate)) { nrCommands = 0; TRI_RunGarbageCollectionV8(_isolate, 500.0); + + // needs to be reset after the garbage collection + V8PlatformFeature::resetOutOfMemory(_isolate); } } diff --git a/lib/ApplicationFeatures/V8PlatformFeature.cpp b/lib/ApplicationFeatures/V8PlatformFeature.cpp index 619135659e..7cca582502 100644 --- a/lib/ApplicationFeatures/V8PlatformFeature.cpp +++ b/lib/ApplicationFeatures/V8PlatformFeature.cpp @@ -22,25 +22,27 @@ #include "ApplicationFeatures/V8PlatformFeature.h" +#include "Basics/StringUtils.h" #include "Logger/Logger.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" using namespace arangodb; +using namespace arangodb::basics; using namespace arangodb::options; namespace { - class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { - public: - virtual void* Allocate(size_t length) override { - void* data = AllocateUninitialized(length); - return data == nullptr ? data : memset(data, 0, length); - } - virtual void* AllocateUninitialized(size_t length) override { - return malloc(length); - } - virtual void Free(void* data, size_t) override { free(data); } - }; +class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { + public: + virtual void* Allocate(size_t length) override { + void* data = AllocateUninitialized(length); + return data == nullptr ? data : memset(data, 0, length); + } + virtual void* AllocateUninitialized(size_t length) override { + return malloc(length); + } + virtual void Free(void* data, size_t) override { free(data); } +}; } V8PlatformFeature::V8PlatformFeature( @@ -56,16 +58,34 @@ void V8PlatformFeature::collectOptions( options->addSection("javascript", "Configure the Javascript engine"); options->addHiddenOption("--javascript.v8-options", "options to pass to v8", - new StringParameter(&_v8options)); + new VectorParameter(&_v8Options)); + + options->addOption("--javascript.v8-max-heap", "maximal heap size", + new UInt64Parameter(&_v8MaxHeap)); +} + +void V8PlatformFeature::validateOptions( + std::shared_ptr options) { + if (!_v8Options.empty()) { + _v8CombinedOptions = StringUtils::join(_v8Options, " "); + + if (_v8CombinedOptions == "help") { + std::string help = "--help"; + v8::V8::SetFlagsFromString(help.c_str(), (int)help.size()); + exit(EXIT_SUCCESS); + } + } } void V8PlatformFeature::start() { v8::V8::InitializeICU(); // explicit option --javascript.v8-options used - if (!_v8options.empty()) { - LOG(INFO) << "using V8 options '" << _v8options << "'"; - v8::V8::SetFlagsFromString(_v8options.c_str(), (int)_v8options.size()); + if (!_v8CombinedOptions.empty()) { + LOG_TOPIC(INFO, Logger::V8) << "using V8 options '" << _v8CombinedOptions + << "'"; + v8::V8::SetFlagsFromString(_v8CombinedOptions.c_str(), + (int)_v8CombinedOptions.size()); } #ifdef TRI_FORCE_ARMV6 @@ -86,3 +106,69 @@ void V8PlatformFeature::unprepare() { _platform.reset(); _allocator.reset(); } + +void gcPrologueCallback(v8::Isolate* isolate, v8::GCType type, + v8::GCCallbackFlags flags) { + // if (type != v8::kGCTypeMarkSweepCompact) { + // return; + // } + + v8::HeapStatistics h; + isolate->GetHeapStatistics(&h); + + V8PlatformFeature::getIsolateData(isolate)->_heapSizeAtStart = + h.used_heap_size(); +} + +void gcEpilogueCallback(v8::Isolate* isolate, v8::GCType type, + v8::GCCallbackFlags flags) { + static size_t const LIMIT_ABS = 200 * 1024 * 1024; + size_t minFreed = LIMIT_ABS / 10; + + if (type != v8::kGCTypeMarkSweepCompact) { + minFreed = 0; + } + + v8::HeapStatistics h; + isolate->GetHeapStatistics(&h); + + size_t freed = 0; + size_t heapSizeAtStop = h.used_heap_size(); + size_t heapSizeAtStart = + V8PlatformFeature::getIsolateData(isolate)->_heapSizeAtStart; + + if (heapSizeAtStop < heapSizeAtStart) { + freed = heapSizeAtStart - heapSizeAtStop; + } + + size_t heapSizeLimit = h.heap_size_limit(); + size_t usedHeadSize = h.used_heap_size(); + size_t stillFree = heapSizeLimit - usedHeadSize; + + if (stillFree <= LIMIT_ABS && freed <= minFreed) { + LOG(WARN) << "reached heap-size limit, interrupting V8 execution (" + << "heap size limit " << heapSizeLimit << ", used " + << usedHeadSize << ")"; + + isolate->TerminateExecution(); + V8PlatformFeature::setOutOfMemory(isolate); + } +} + +v8::Isolate* V8PlatformFeature::createIsolate() { + v8::Isolate::CreateParams createParams; + createParams.array_buffer_allocator = _allocator.get(); + + if (0 < _v8MaxHeap) { + createParams.constraints.set_max_old_space_size(_v8MaxHeap); + } + + auto isolate = v8::Isolate::New(createParams); + isolate->AddGCPrologueCallback(gcPrologueCallback); + isolate->AddGCEpilogueCallback(gcEpilogueCallback); + + _isolateData.emplace_back(new IsolateData()); + isolate->SetData(V8_INFO, _isolateData.back().get()); + + return isolate; +} diff --git a/lib/ApplicationFeatures/V8PlatformFeature.h b/lib/ApplicationFeatures/V8PlatformFeature.h index c8c2cbb3c0..3e3954127e 100644 --- a/lib/ApplicationFeatures/V8PlatformFeature.h +++ b/lib/ApplicationFeatures/V8PlatformFeature.h @@ -25,30 +25,60 @@ #include "ApplicationFeatures/ApplicationFeature.h" -#include #include +#include namespace arangodb { class V8PlatformFeature final : public application_features::ApplicationFeature { + private: + struct IsolateData { + bool _outOfMemory = false; + size_t _heapSizeAtStart = 0; + }; + + public: + static IsolateData* getIsolateData(v8::Isolate* isolate) { + return reinterpret_cast(isolate->GetData(V8_INFO)); + } + + static bool isOutOfMemory(v8::Isolate* isolate) { + return getIsolateData(isolate)->_outOfMemory; + } + + static void setOutOfMemory(v8::Isolate* isolate) { + getIsolateData(isolate)->_outOfMemory = true; + } + + static void resetOutOfMemory(v8::Isolate* isolate) { + getIsolateData(isolate)->_outOfMemory = false; + } + + public: + static const uint32_t V8_INFO = 0; + static const uint32_t V8_DATA_SLOT = 1; + public: explicit V8PlatformFeature(application_features::ApplicationServer* server); public: void collectOptions(std::shared_ptr) override final; + void validateOptions(std::shared_ptr) override final; void start() override final; void unprepare() override final; - v8::ArrayBuffer::Allocator* arrayBufferAllocator() const { - return _allocator.get(); - } - private: - std::string _v8options; + std::vector _v8Options; + uint64_t _v8MaxHeap = 3 * 1024; + + public: + v8::Isolate* createIsolate(); private: std::unique_ptr _platform; std::unique_ptr _allocator; + std::string _v8CombinedOptions; + std::vector> _isolateData; }; } diff --git a/lib/V8/v8-globals.cpp b/lib/V8/v8-globals.cpp index 6b1bb2efc2..cc2fb4de73 100644 --- a/lib/V8/v8-globals.cpp +++ b/lib/V8/v8-globals.cpp @@ -218,7 +218,7 @@ TRI_v8_global_t* TRI_CreateV8Globals(v8::Isolate* isolate) { TRI_ASSERT(v8g == nullptr); v8g = new TRI_v8_global_t(isolate); - isolate->SetData(V8DataSlot, v8g); + isolate->SetData(arangodb::V8PlatformFeature::V8_DATA_SLOT, v8g); return v8g; } diff --git a/lib/V8/v8-globals.h b/lib/V8/v8-globals.h index a4ae366c34..e004c56c08 100644 --- a/lib/V8/v8-globals.h +++ b/lib/V8/v8-globals.h @@ -26,12 +26,10 @@ #include "Basics/Common.h" -#include +#include "ApplicationFeatures/V8PlatformFeature.h" struct TRI_vocbase_t; -static const uint32_t V8DataSlot = 0; - //////////////////////////////////////////////////////////////////////////////// /// @brief shortcut for fetching the isolate from the thread context //////////////////////////////////////////////////////////////////////////////// @@ -45,7 +43,6 @@ static const uint32_t V8DataSlot = 0; #define TRI_V8_TRY_CATCH_BEGIN(isolateVar) \ auto isolateVar = args.GetIsolate(); \ try { - //////////////////////////////////////////////////////////////////////////////// /// @brief macro to terminate a try-catch sequence for V8 callbacks //////////////////////////////////////////////////////////////////////////////// @@ -76,7 +73,7 @@ static const uint32_t V8DataSlot = 0; v8::String::NewFromOneByte(isolate, (uint8_t const*)(name), \ v8::String::kNormalString, (int)strlen(name)) -#define TRI_V8_ASCII_STD_STRING(isolate, name) \ +#define TRI_V8_ASCII_STD_STRING(isolate, name) \ v8::String::NewFromOneByte(isolate, (uint8_t const*)(name.c_str()), \ v8::String::kNormalString, (int)name.size()) @@ -135,11 +132,11 @@ static const uint32_t V8DataSlot = 0; /// @brief shortcut for current v8 globals and scope //////////////////////////////////////////////////////////////////////////////// -#define TRI_V8_CURRENT_GLOBALS_AND_SCOPE \ - TRI_v8_global_t* v8g = \ - static_cast(isolate->GetData(V8DataSlot)); \ - v8::HandleScope scope(isolate); \ - do { \ +#define TRI_V8_CURRENT_GLOBALS_AND_SCOPE \ + TRI_v8_global_t* v8g = static_cast( \ + isolate->GetData(arangodb::V8PlatformFeature::V8_DATA_SLOT)); \ + v8::HandleScope scope(isolate); \ + do { \ } while (0) //////////////////////////////////////////////////////////////////////////////// @@ -403,13 +400,13 @@ static const uint32_t V8DataSlot = 0; /// implicitly requires 'isolate' to be available //////////////////////////////////////////////////////////////////////////////// -#define TRI_GET_GLOBALS() \ - TRI_v8_global_t* v8g = \ - static_cast(isolate->GetData(V8DataSlot)) +#define TRI_GET_GLOBALS() \ + TRI_v8_global_t* v8g = static_cast( \ + isolate->GetData(arangodb::V8PlatformFeature::V8_DATA_SLOT)) -#define TRI_GET_GLOBALS2(isolate) \ - TRI_v8_global_t* v8g = \ - static_cast(isolate->GetData(V8DataSlot)) +#define TRI_GET_GLOBALS2(isolate) \ + TRI_v8_global_t* v8g = static_cast( \ + isolate->GetData(arangodb::V8PlatformFeature::V8_DATA_SLOT)) //////////////////////////////////////////////////////////////////////////////// /// @brief fetch a string-member from the global into the local scope of the diff --git a/lib/V8/v8-vpack.cpp b/lib/V8/v8-vpack.cpp index e8a74e5f34..1f9170536f 100644 --- a/lib/V8/v8-vpack.cpp +++ b/lib/V8/v8-vpack.cpp @@ -22,14 +22,16 @@ //////////////////////////////////////////////////////////////////////////////// #include "v8-vpack.h" + +#include +#include + +#include "ApplicationFeatures/V8PlatformFeature.h" #include "Basics/Exceptions.h" #include "Basics/StringRef.h" #include "Basics/VelocyPackHelper.h" #include "V8/v8-utils.h" -#include -#include - using VelocyPackHelper = arangodb::basics::VelocyPackHelper; /// @brief maximum object nesting depth @@ -63,7 +65,7 @@ static v8::Handle ObjectVPackObject(v8::Isolate* isolate, if (object.IsEmpty()) { return v8::Undefined(isolate); } - + TRI_GET_GLOBALS(); VPackObjectIterator it(slice, true); @@ -74,7 +76,8 @@ static v8::Handle ObjectVPackObject(v8::Isolate* isolate, if (k.isString()) { // regular attribute char const* p = k.getString(l); - object->ForceSet(TRI_V8_PAIR_STRING(p, l), TRI_VPackToV8(isolate, it.value(), options, &slice)); + object->ForceSet(TRI_V8_PAIR_STRING(p, l), + TRI_VPackToV8(isolate, it.value(), options, &slice)); } else { // optimized code path for translated system attributes VPackSlice v = VPackSlice(k.begin() + 1); @@ -86,31 +89,41 @@ static v8::Handle ObjectVPackObject(v8::Isolate* isolate, sub = TRI_VPackToV8(isolate, v, options, &slice); } - uint8_t which = static_cast(k.getUInt()) + VelocyPackHelper::AttributeBase; + uint8_t which = + static_cast(k.getUInt()) + VelocyPackHelper::AttributeBase; switch (which) { - case VelocyPackHelper::KeyAttribute: { - object->ForceSet(v8::Local::New(isolate, v8g->_KeyKey), sub); + case VelocyPackHelper::KeyAttribute: { + object->ForceSet(v8::Local::New(isolate, v8g->_KeyKey), + sub); break; } - case VelocyPackHelper::RevAttribute: { - object->ForceSet(v8::Local::New(isolate, v8g->_RevKey), sub); + case VelocyPackHelper::RevAttribute: { + object->ForceSet(v8::Local::New(isolate, v8g->_RevKey), + sub); break; } case VelocyPackHelper::IdAttribute: { - object->ForceSet(v8::Local::New(isolate, v8g->_IdKey), sub); + object->ForceSet(v8::Local::New(isolate, v8g->_IdKey), + sub); break; } case VelocyPackHelper::FromAttribute: { - object->ForceSet(v8::Local::New(isolate, v8g->_FromKey), sub); + object->ForceSet(v8::Local::New(isolate, v8g->_FromKey), + sub); break; } case VelocyPackHelper::ToAttribute: { - object->ForceSet(v8::Local::New(isolate, v8g->_ToKey), sub); + object->ForceSet(v8::Local::New(isolate, v8g->_ToKey), + sub); break; } } } + if (arangodb::V8PlatformFeature::isOutOfMemory(isolate)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } + it.next(); } @@ -142,6 +155,9 @@ static v8::Handle ObjectVPackArray(v8::Isolate* isolate, if (!val.IsEmpty()) { object->Set(j++, val); } + if (arangodb::V8PlatformFeature::isOutOfMemory(isolate)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } it.next(); } @@ -166,7 +182,8 @@ v8::Handle TRI_VPackToV8(v8::Isolate* isolate, case VPackValueType::Double: { // convert NaN, +inf & -inf to null double value = slice.getDouble(); - if (std::isnan(value) || !std::isfinite(value) || value == HUGE_VAL || value == -HUGE_VAL) { + if (std::isnan(value) || !std::isfinite(value) || value == HUGE_VAL || + value == -HUGE_VAL) { return v8::Null(isolate); } return v8::Number::New(isolate, slice.getDouble()); @@ -179,7 +196,8 @@ v8::Handle TRI_VPackToV8(v8::Isolate* isolate, } if (value >= 0 && value <= 4294967295LL) { // value is within bounds of a uint32_t - return v8::Integer::NewFromUnsigned(isolate, static_cast(value)); + return v8::Integer::NewFromUnsigned(isolate, + static_cast(value)); } // must use double to avoid truncation return v8::Number::New(isolate, static_cast(slice.getInt())); @@ -188,7 +206,8 @@ v8::Handle TRI_VPackToV8(v8::Isolate* isolate, uint64_t value = slice.getUInt(); if (value <= 4294967295ULL) { // value is within bounds of a uint32_t - return v8::Integer::NewFromUnsigned(isolate, static_cast(value)); + return v8::Integer::NewFromUnsigned(isolate, + static_cast(value)); } // must use double to avoid truncation return v8::Number::New(isolate, static_cast(slice.getUInt())); @@ -207,10 +226,12 @@ v8::Handle TRI_VPackToV8(v8::Isolate* isolate, } case VPackValueType::External: { // resolve external - return TRI_VPackToV8(isolate, VPackSlice(slice.getExternal()), options, base); + return TRI_VPackToV8(isolate, VPackSlice(slice.getExternal()), options, + base); } case VPackValueType::Custom: { - if (options == nullptr || options->customTypeHandler == nullptr || base == nullptr) { + if (options == nullptr || options->customTypeHandler == nullptr || + base == nullptr) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "Could not extract custom attribute."); } @@ -219,9 +240,7 @@ v8::Handle TRI_VPackToV8(v8::Isolate* isolate, return TRI_V8_STD_STRING(id); } case VPackValueType::None: - default: { - return v8::Undefined(isolate); - } + default: { return v8::Undefined(isolate); } } } @@ -244,8 +263,9 @@ struct BuilderContext { /// @brief adds a VPackValue to either an array or an object //////////////////////////////////////////////////////////////////////////////// -template -static inline void AddValue(BuilderContext& context, arangodb::StringRef const& attributeName, +template +static inline void AddValue(BuilderContext& context, + arangodb::StringRef const& attributeName, T const& value) { if (inObject) { context.builder.add(attributeName.begin(), attributeName.size(), value); @@ -262,34 +282,33 @@ template static int V8ToVPack(BuilderContext& context, v8::Handle const parameter, arangodb::StringRef const& attributeName) { - if (parameter->IsNull() || parameter->IsUndefined()) { AddValue(context, attributeName, - VPackValue(VPackValueType::Null)); + VPackValue(VPackValueType::Null)); return TRI_ERROR_NO_ERROR; } if (parameter->IsBoolean()) { AddValue(context, attributeName, - VPackValue(parameter->ToBoolean()->Value())); + VPackValue(parameter->ToBoolean()->Value())); return TRI_ERROR_NO_ERROR; } - + if (parameter->IsNumber()) { if (parameter->IsInt32()) { AddValue(context, attributeName, - VPackValue(parameter->ToInt32()->Value())); + VPackValue(parameter->ToInt32()->Value())); return TRI_ERROR_NO_ERROR; } - + if (parameter->IsUint32()) { - AddValue(context, attributeName, - VPackValue(parameter->ToUint32()->Value())); + AddValue( + context, attributeName, VPackValue(parameter->ToUint32()->Value())); return TRI_ERROR_NO_ERROR; } AddValue(context, attributeName, - VPackValue(parameter->ToNumber()->Value())); + VPackValue(parameter->ToNumber()->Value())); return TRI_ERROR_NO_ERROR; } @@ -300,7 +319,9 @@ static int V8ToVPack(BuilderContext& context, return TRI_ERROR_OUT_OF_MEMORY; } - AddValue(context, attributeName, VPackValuePair(*str, str.length(), VPackValueType::String)); + AddValue( + context, attributeName, + VPackValuePair(*str, str.length(), VPackValueType::String)); return TRI_ERROR_NO_ERROR; } @@ -308,7 +329,7 @@ static int V8ToVPack(BuilderContext& context, v8::Handle array = v8::Handle::Cast(parameter); AddValue(context, attributeName, - VPackValue(VPackValueType::Array)); + VPackValue(VPackValueType::Array)); uint32_t const n = array->Length(); for (uint32_t i = 0; i < n; ++i) { @@ -323,8 +344,9 @@ static int V8ToVPack(BuilderContext& context, return TRI_ERROR_BAD_PARAMETER; } - int res = V8ToVPack(context, value, arangodb::StringRef()); - + int res = V8ToVPack(context, value, + arangodb::StringRef()); + --context.level; if (res != TRI_ERROR_NO_ERROR) { @@ -341,16 +363,18 @@ static int V8ToVPack(BuilderContext& context, if (parameter->IsObject()) { if (performAllChecks) { if (parameter->IsBooleanObject()) { - AddValue(context, attributeName, - VPackValue(v8::Handle::Cast(parameter) - ->BooleanValue())); + AddValue( + context, attributeName, + VPackValue(v8::Handle::Cast(parameter) + ->BooleanValue())); return TRI_ERROR_NO_ERROR; } if (parameter->IsNumberObject()) { - AddValue(context, attributeName, - VPackValue(v8::Handle::Cast(parameter) - ->NumberValue())); + AddValue( + context, attributeName, + VPackValue( + v8::Handle::Cast(parameter)->NumberValue())); return TRI_ERROR_NO_ERROR; } @@ -361,7 +385,9 @@ static int V8ToVPack(BuilderContext& context, return TRI_ERROR_OUT_OF_MEMORY; } - AddValue(context, attributeName, VPackValuePair(*str, str.length(), VPackValueType::String)); + AddValue( + context, attributeName, + VPackValuePair(*str, str.length(), VPackValueType::String)); return TRI_ERROR_NO_ERROR; } @@ -379,7 +405,8 @@ static int V8ToVPack(BuilderContext& context, // call it if yes v8::Handle func = o->Get(context.toJsonKey); if (func->IsFunction()) { - v8::Handle toJson = v8::Handle::Cast(func); + v8::Handle toJson = + v8::Handle::Cast(func); v8::Handle args; v8::Handle converted = toJson->Call(o, 0, &args); @@ -393,7 +420,9 @@ static int V8ToVPack(BuilderContext& context, } // this passes ownership for the utf8 string to the JSON object - AddValue(context, attributeName, VPackValuePair(*str, str.length(), VPackValueType::String)); + AddValue( + context, attributeName, + VPackValuePair(*str, str.length(), VPackValueType::String)); return TRI_ERROR_NO_ERROR; } } @@ -406,7 +435,7 @@ static int V8ToVPack(BuilderContext& context, uint32_t const n = names->Length(); AddValue(context, attributeName, - VPackValue(VPackValueType::Object)); + VPackValue(VPackValueType::Object)); for (uint32_t i = 0; i < n; ++i) { // process attribute name @@ -428,8 +457,9 @@ static int V8ToVPack(BuilderContext& context, return TRI_ERROR_BAD_PARAMETER; } - int res = V8ToVPack(context, value, arangodb::StringRef(*str, str.length())); - + int res = V8ToVPack( + context, value, arangodb::StringRef(*str, str.length())); + --context.level; if (res != TRI_ERROR_NO_ERROR) { @@ -466,10 +496,10 @@ int TRI_V8ToVPack(v8::Isolate* isolate, VPackBuilder& builder, /// does not contain types such as Function, Date or RegExp //////////////////////////////////////////////////////////////////////////////// -int TRI_V8ToVPackSimple(v8::Isolate* isolate, arangodb::velocypack::Builder& builder, +int TRI_V8ToVPackSimple(v8::Isolate* isolate, + arangodb::velocypack::Builder& builder, v8::Handle const value) { // a HandleScope must have been created by the caller already BuilderContext context(isolate, builder, false); return V8ToVPack(context, value, arangodb::StringRef()); } -