1
0
Fork 0

added a panic interrupt of memory gets low

This commit is contained in:
Frank Celler 2016-07-31 12:22:20 +02:00
parent 792f42d5af
commit 51683dbda0
12 changed files with 409 additions and 210 deletions

View File

@ -1,6 +1,10 @@
devel devel
----- -----
* added a memory expection of V8 memory gets to low
* fixed epoch computation in hybrid logical clock
* fixed thread affinity * fixed thread affinity
* replaced require("internal").db by require("@arangodb").db * replaced require("internal").db by require("@arangodb").db

View File

@ -147,9 +147,8 @@ size_t AqlValue::length() const {
} }
/// @brief get the (array) element at position /// @brief get the (array) element at position
AqlValue AqlValue::at(arangodb::AqlTransaction* trx, AqlValue AqlValue::at(arangodb::AqlTransaction* trx, int64_t position,
int64_t position, bool& mustDestroy, bool& mustDestroy, bool doCopy) const {
bool doCopy) const {
mustDestroy = false; mustDestroy = false;
switch (type()) { switch (type()) {
case VPACK_SLICE_POINTER: case VPACK_SLICE_POINTER:
@ -190,7 +189,9 @@ AqlValue AqlValue::at(arangodb::AqlTransaction* trx,
// found the correct vector // found the correct vector
if (doCopy) { if (doCopy) {
mustDestroy = true; mustDestroy = true;
return it->getValueReference(static_cast<size_t>(position - total), 0).clone(); return it
->getValueReference(static_cast<size_t>(position - total), 0)
.clone();
} }
return it->getValue(static_cast<size_t>(position - total), 0); return it->getValue(static_cast<size_t>(position - total), 0);
} }
@ -210,7 +211,8 @@ AqlValue AqlValue::at(arangodb::AqlTransaction* trx,
if (position >= 0 && position < static_cast<int64_t>(n)) { if (position >= 0 && position < static_cast<int64_t>(n)) {
// only look up the value if it is within array bounds // only look up the value if it is within array bounds
TransactionBuilderLeaser builder(trx); TransactionBuilderLeaser builder(trx);
builder->add(VPackValue(_data.range->at(static_cast<size_t>(position)))); builder->add(
VPackValue(_data.range->at(static_cast<size_t>(position))));
mustDestroy = true; mustDestroy = true;
return AqlValue(builder->slice()); return AqlValue(builder->slice());
} }
@ -373,9 +375,8 @@ AqlValue AqlValue::getToAttribute(arangodb::AqlTransaction* trx,
} }
/// @brief get the (object) element by name /// @brief get the (object) element by name
AqlValue AqlValue::get(arangodb::AqlTransaction* trx, AqlValue AqlValue::get(arangodb::AqlTransaction* trx, std::string const& name,
std::string const& name, bool& mustDestroy, bool& mustDestroy, bool doCopy) const {
bool doCopy) const {
mustDestroy = false; mustDestroy = false;
switch (type()) { switch (type()) {
case VPACK_SLICE_POINTER: case VPACK_SLICE_POINTER:
@ -416,8 +417,8 @@ AqlValue AqlValue::get(arangodb::AqlTransaction* trx,
/// @brief get the (object) element(s) by name /// @brief get the (object) element(s) by name
AqlValue AqlValue::get(arangodb::AqlTransaction* trx, AqlValue AqlValue::get(arangodb::AqlTransaction* trx,
std::vector<std::string> const& names, std::vector<std::string> const& names, bool& mustDestroy,
bool& mustDestroy, bool doCopy) const { bool doCopy) const {
mustDestroy = false; mustDestroy = false;
if (names.empty()) { if (names.empty()) {
return AqlValue(arangodb::basics::VelocyPackHelper::NullValue()); return AqlValue(arangodb::basics::VelocyPackHelper::NullValue());
@ -549,8 +550,7 @@ double AqlValue::toDouble(arangodb::AqlTransaction* trx, bool& failed) const {
// conversion failed // conversion failed
break; break;
} }
} } else if (s.isArray()) {
else if (s.isArray()) {
auto length = s.length(); auto length = s.length();
if (length == 0) { if (length == 0) {
return 0.0; return 0.0;
@ -601,8 +601,7 @@ int64_t AqlValue::toInt64(arangodb::AqlTransaction* trx) const {
} }
// conversion failed // conversion failed
} }
} } else if (s.isArray()) {
else if (s.isArray()) {
auto length = s.length(); auto length = s.length();
if (length == 0) { if (length == 0) {
return 0; return 0;
@ -723,9 +722,8 @@ v8::Handle<v8::Value> AqlValue::toV8Partial(
} }
/// @brief construct a V8 value as input for the expression execution in V8 /// @brief construct a V8 value as input for the expression execution in V8
v8::Handle<v8::Value> AqlValue::toV8( v8::Handle<v8::Value> AqlValue::toV8(v8::Isolate* isolate,
v8::Isolate* isolate, arangodb::AqlTransaction* trx) const { arangodb::AqlTransaction* trx) const {
switch (type()) { switch (type()) {
case VPACK_SLICE_POINTER: case VPACK_SLICE_POINTER:
case VPACK_INLINE: case VPACK_INLINE:
@ -744,6 +742,10 @@ v8::Handle<v8::Value> AqlValue::toV8(
size_t const n = it->size(); size_t const n = it->size();
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
result->Set(j++, it->getValueReference(i, 0).toV8(isolate, trx)); result->Set(j++, it->getValueReference(i, 0).toV8(isolate, trx));
if (V8PlatformFeature::isOutOfMemory(isolate)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
} }
} }
return result; return result;
@ -755,8 +757,15 @@ v8::Handle<v8::Value> AqlValue::toV8(
for (uint32_t i = 0; i < n; ++i) { for (uint32_t i = 0; i < n; ++i) {
// is it safe to use a double here (precision loss)? // is it safe to use a double here (precision loss)?
result->Set(i, v8::Number::New(isolate, result->Set(
static_cast<double>(_data.range->at(static_cast<size_t>(i))))); i, v8::Number::New(isolate, static_cast<double>(_data.range->at(
static_cast<size_t>(i)))));
if (i % 1000 == 0) {
if (V8PlatformFeature::isOutOfMemory(isolate)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
}
} }
return result; return result;
} }
@ -790,7 +799,8 @@ void AqlValue::toVelocyPack(AqlTransaction* trx,
for (auto const& it : *_data.docvec) { for (auto const& it : *_data.docvec) {
size_t const n = it->size(); size_t const n = it->size();
for (size_t i = 0; i < n; ++i) { 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(); builder.close();
@ -809,7 +819,8 @@ void AqlValue::toVelocyPack(AqlTransaction* trx,
} }
/// @brief materializes a value into the builder /// @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()) { switch (type()) {
case VPACK_SLICE_POINTER: case VPACK_SLICE_POINTER:
case VPACK_INLINE: case VPACK_INLINE:
@ -821,7 +832,8 @@ AqlValue AqlValue::materialize(AqlTransaction* trx, bool& hasCopied, bool resolv
case RANGE: { case RANGE: {
bool shouldDelete = true; bool shouldDelete = true;
ConditionalDeleter<VPackBuffer<uint8_t>> deleter(shouldDelete); ConditionalDeleter<VPackBuffer<uint8_t>> deleter(shouldDelete);
std::shared_ptr<VPackBuffer<uint8_t>> buffer(new VPackBuffer<uint8_t>, deleter); std::shared_ptr<VPackBuffer<uint8_t>> buffer(new VPackBuffer<uint8_t>,
deleter);
VPackBuilder builder(buffer); VPackBuilder builder(buffer);
toVelocyPack(trx, builder, resolveExternals); toVelocyPack(trx, builder, resolveExternals);
hasCopied = true; hasCopied = true;
@ -853,7 +865,8 @@ AqlValue AqlValue::clone() const {
// copy buffer // copy buffer
VPackValueLength length = _data.buffer->size(); VPackValueLength length = _data.buffer->size();
auto buffer = new VPackBuffer<uint8_t>(length); auto buffer = new VPackBuffer<uint8_t>(length);
buffer->append(reinterpret_cast<char const*>(_data.buffer->data()), length); buffer->append(reinterpret_cast<char const*>(_data.buffer->data()),
length);
return AqlValue(buffer); return AqlValue(buffer);
} }
case DOCVEC: { case DOCVEC: {
@ -941,10 +954,10 @@ VPackSlice AqlValue::slice() const {
AqlValue AqlValue::CreateFromBlocks( AqlValue AqlValue::CreateFromBlocks(
arangodb::AqlTransaction* trx, std::vector<AqlItemBlock*> const& src, arangodb::AqlTransaction* trx, std::vector<AqlItemBlock*> const& src,
std::vector<std::string> const& variableNames) { std::vector<std::string> const& variableNames) {
bool shouldDelete = true; bool shouldDelete = true;
ConditionalDeleter<VPackBuffer<uint8_t>> deleter(shouldDelete); ConditionalDeleter<VPackBuffer<uint8_t>> deleter(shouldDelete);
std::shared_ptr<VPackBuffer<uint8_t>> buffer(new VPackBuffer<uint8_t>, deleter); std::shared_ptr<VPackBuffer<uint8_t>> buffer(new VPackBuffer<uint8_t>,
deleter);
VPackBuilder builder(buffer); VPackBuilder builder(buffer);
builder.openArray(); builder.openArray();
@ -980,17 +993,18 @@ AqlValue AqlValue::CreateFromBlocks(
AqlValue AqlValue::CreateFromBlocks( AqlValue AqlValue::CreateFromBlocks(
arangodb::AqlTransaction* trx, std::vector<AqlItemBlock*> const& src, arangodb::AqlTransaction* trx, std::vector<AqlItemBlock*> const& src,
arangodb::aql::RegisterId expressionRegister) { arangodb::aql::RegisterId expressionRegister) {
bool shouldDelete = true; bool shouldDelete = true;
ConditionalDeleter<VPackBuffer<uint8_t>> deleter(shouldDelete); ConditionalDeleter<VPackBuffer<uint8_t>> deleter(shouldDelete);
std::shared_ptr<VPackBuffer<uint8_t>> buffer(new VPackBuffer<uint8_t>, deleter); std::shared_ptr<VPackBuffer<uint8_t>> buffer(new VPackBuffer<uint8_t>,
deleter);
VPackBuilder builder(buffer); VPackBuilder builder(buffer);
builder.openArray(); builder.openArray();
for (auto const& current : src) { for (auto const& current : src) {
for (size_t i = 0; i < current->size(); ++i) { 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,15 +1014,15 @@ AqlValue AqlValue::CreateFromBlocks(
/// @brief 3-way comparison for AqlValue objects /// @brief 3-way comparison for AqlValue objects
int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left, int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left,
AqlValue const& right, AqlValue const& right, bool compareUtf8) {
bool compareUtf8) {
VPackOptions* options = trx->transactionContext()->getVPackOptions(); VPackOptions* options = trx->transactionContext()->getVPackOptions();
AqlValue::AqlValueType const leftType = left.type(); AqlValue::AqlValueType const leftType = left.type();
AqlValue::AqlValueType const rightType = right.type(); AqlValue::AqlValueType const rightType = right.type();
if (leftType != rightType) { 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 // range|docvec against x
VPackBuilder leftBuilder; VPackBuilder leftBuilder;
left.toVelocyPack(trx, leftBuilder, false); left.toVelocyPack(trx, leftBuilder, false);
@ -1016,7 +1030,8 @@ int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left,
VPackBuilder rightBuilder; VPackBuilder rightBuilder;
right.toVelocyPack(trx, rightBuilder, false); 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 // 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_SLICE_POINTER:
case VPACK_INLINE: case VPACK_INLINE:
case VPACK_MANAGED: { 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: { case DOCVEC: {
// use lexicographic ordering of AqlValues regardless of block, // 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(); size_t rrows = right._data.docvec->at(0)->size();
while (lblock < lsize && rblock < rsize) { while (lblock < lsize && rblock < rsize) {
AqlValue const& lval = left._data.docvec->at(lblock)->getValueReference(litem, 0); AqlValue const& lval =
AqlValue const& rval = right._data.docvec->at(rblock)->getValueReference(ritem, 0); 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); int cmp = Compare(trx, lval, rval, compareUtf8);
@ -1101,4 +1119,3 @@ int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left,
return 0; return 0;
} }

View File

@ -721,8 +721,7 @@ QueryResult Query::execute(QueryRegistry* registry) {
} }
} }
/// @brief execute an AQL query // execute an AQL query: may only be called with an active V8 handle scope
/// may only be called with an active V8 handle scope
QueryResultV8 Query::executeV8(v8::Isolate* isolate, QueryRegistry* registry) { QueryResultV8 Query::executeV8(v8::Isolate* isolate, QueryRegistry* registry) {
LOG_TOPIC(DEBUG, Logger::QUERIES) << TRI_microtime() - _startTime << " " LOG_TOPIC(DEBUG, Logger::QUERIES) << TRI_microtime() - _startTime << " "
<< "Query::executeV8" << " this: " << (uintptr_t) this; << "Query::executeV8" << " this: " << (uintptr_t) this;
@ -829,6 +828,10 @@ QueryResultV8 Query::executeV8(v8::Isolate* isolate, QueryRegistry* registry) {
if (!val.isEmpty()) { if (!val.isEmpty()) {
result.result->Set(j++, val.toV8(isolate, _trx)); result.result->Set(j++, val.toV8(isolate, _trx));
} }
if (V8PlatformFeature::isOutOfMemory(isolate)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
} }
} }
delete value; delete value;

View File

@ -23,8 +23,8 @@
#include "ConsoleThread.h" #include "ConsoleThread.h"
#include <iostream>
#include <v8.h> #include <v8.h>
#include <iostream>
#include "ApplicationFeatures/ApplicationServer.h" #include "ApplicationFeatures/ApplicationServer.h"
#include "Basics/MutexLocker.h" #include "Basics/MutexLocker.h"
@ -157,14 +157,20 @@ start_pretty_print();
} }
while (!isStopping() && !_userAborted.load()) { while (!isStopping() && !_userAborted.load()) {
if (nrCommands >= gcInterval) { if (nrCommands >= gcInterval ||
V8PlatformFeature::isOutOfMemory(isolate)) {
TRI_RunGarbageCollectionV8(isolate, 0.5); TRI_RunGarbageCollectionV8(isolate, 0.5);
nrCommands = 0; nrCommands = 0;
// needs to be reset after the garbage collection
V8PlatformFeature::resetOutOfMemory(isolate);
} }
std::string input; std::string input;
bool eof; bool eof;
isolate->CancelTerminateExecution();
{ {
MUTEX_LOCKER(mutexLocker, serverConsoleMutex); MUTEX_LOCKER(mutexLocker, serverConsoleMutex);
input = console.prompt("arangod> ", "arangod", eof); input = console.prompt("arangod> ", "arangod", eof);

View File

@ -25,8 +25,8 @@
#include "Utils/CollectionNameResolver.h" #include "Utils/CollectionNameResolver.h"
#include "VocBase/transaction.h" #include "VocBase/transaction.h"
#include "V8/v8-globals.h"
#include <v8.h> #include <v8.h>
#include "V8/v8-globals.h"
using namespace arangodb; using namespace arangodb;
@ -34,28 +34,31 @@ using namespace arangodb;
/// @brief create the context /// @brief create the context
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
V8TransactionContext::V8TransactionContext(TRI_vocbase_t* vocbase, bool embeddable) V8TransactionContext::V8TransactionContext(TRI_vocbase_t* vocbase,
bool embeddable)
: TransactionContext(vocbase), : TransactionContext(vocbase),
_sharedTransactionContext(static_cast<V8TransactionContext*>( _sharedTransactionContext(static_cast<V8TransactionContext*>(
static_cast<TRI_v8_global_t*>(v8::Isolate::GetCurrent()->GetData( static_cast<TRI_v8_global_t*>(v8::Isolate::GetCurrent()->GetData(
V8DataSlot))->_transactionContext)), V8PlatformFeature::V8_DATA_SLOT))
->_transactionContext)),
_mainScope(nullptr), _mainScope(nullptr),
_currentTransaction(nullptr), _currentTransaction(nullptr),
_embeddable(embeddable) { _embeddable(embeddable) {}
}
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief order a custom type handler for the collection /// @brief order a custom type handler for the collection
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
std::shared_ptr<VPackCustomTypeHandler> V8TransactionContext::orderCustomTypeHandler() { std::shared_ptr<VPackCustomTypeHandler>
V8TransactionContext::orderCustomTypeHandler() {
if (_customTypeHandler == nullptr) { if (_customTypeHandler == nullptr) {
V8TransactionContext* main = _sharedTransactionContext->_mainScope; V8TransactionContext* main = _sharedTransactionContext->_mainScope;
if (main != nullptr && main != this && !main->isGlobal()) { if (main != nullptr && main != this && !main->isGlobal()) {
_customTypeHandler = main->orderCustomTypeHandler(); _customTypeHandler = main->orderCustomTypeHandler();
} else { } else {
_customTypeHandler.reset(TransactionContext::createCustomTypeHandler(_vocbase, getResolver())); _customTypeHandler.reset(
TransactionContext::createCustomTypeHandler(_vocbase, getResolver()));
} }
_options.customTypeHandler = _customTypeHandler.get(); _options.customTypeHandler = _customTypeHandler.get();
_dumpOptions.customTypeHandler = _customTypeHandler.get(); _dumpOptions.customTypeHandler = _customTypeHandler.get();
@ -147,7 +150,7 @@ bool V8TransactionContext::isGlobal() const {
bool V8TransactionContext::IsEmbedded() { bool V8TransactionContext::IsEmbedded() {
TRI_v8_global_t* v8g = static_cast<TRI_v8_global_t*>( TRI_v8_global_t* v8g = static_cast<TRI_v8_global_t*>(
v8::Isolate::GetCurrent()->GetData(V8DataSlot)); v8::Isolate::GetCurrent()->GetData(V8PlatformFeature::V8_DATA_SLOT));
if (v8g->_transactionContext == nullptr) { if (v8g->_transactionContext == nullptr) {
return false; return false;
} }
@ -159,7 +162,7 @@ bool V8TransactionContext::IsEmbedded() {
/// @brief create a context, returned in a shared ptr /// @brief create a context, returned in a shared ptr
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<V8TransactionContext> V8TransactionContext::Create(TRI_vocbase_t* vocbase, bool embeddable) { std::shared_ptr<V8TransactionContext> V8TransactionContext::Create(
TRI_vocbase_t* vocbase, bool embeddable) {
return std::make_shared<V8TransactionContext>(vocbase, embeddable); return std::make_shared<V8TransactionContext>(vocbase, embeddable);
} }

View File

@ -568,6 +568,27 @@ void V8DealerFeature::exitContext(V8Context* context) {
bool canceled = false; bool canceled = false;
if (V8PlatformFeature::isOutOfMemory(isolate)) {
static double const availableTime = 300.0;
v8::HandleScope scope(isolate);
{
auto localContext =
v8::Local<v8::Context>::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 // update data for later garbage collection
{ {
TRI_GET_GLOBALS(); TRI_GET_GLOBALS();
@ -858,10 +879,7 @@ void V8DealerFeature::initializeContext(size_t i) {
"V8Platform"); "V8Platform");
TRI_ASSERT(v8platform != nullptr); TRI_ASSERT(v8platform != nullptr);
v8::Isolate::CreateParams createParams; v8::Isolate* isolate = v8platform->createIsolate();
createParams.array_buffer_allocator = v8platform->arrayBufferAllocator();
v8::Isolate* isolate = v8::Isolate::New(createParams);
V8Context* context = _contexts[i] = new V8Context(); V8Context* context = _contexts[i] = new V8Context();
TRI_ASSERT(context->_locker == nullptr); TRI_ASSERT(context->_locker == nullptr);

View File

@ -83,7 +83,6 @@ void V8ShellFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
void V8ShellFeature::validateOptions( void V8ShellFeature::validateOptions(
std::shared_ptr<options::ProgramOptions> options) { std::shared_ptr<options::ProgramOptions> options) {
if (_startupDirectory.empty()) { if (_startupDirectory.empty()) {
LOG(FATAL) << "'--javascript.startup-directory' is empty, giving up"; LOG(FATAL) << "'--javascript.startup-directory' is empty, giving up";
FATAL_ERROR_EXIT(); FATAL_ERROR_EXIT();
@ -94,12 +93,14 @@ void V8ShellFeature::validateOptions(
} }
void V8ShellFeature::start() { void V8ShellFeature::start() {
_console = application_features::ApplicationServer::getFeature<ConsoleFeature>("Console"); _console =
auto platform = application_features::ApplicationServer::getFeature<V8PlatformFeature>("V8Platform"); application_features::ApplicationServer::getFeature<ConsoleFeature>(
"Console");
auto platform =
application_features::ApplicationServer::getFeature<V8PlatformFeature>(
"V8Platform");
v8::Isolate::CreateParams createParams; _isolate = platform->createIsolate();
createParams.array_buffer_allocator = platform->arrayBufferAllocator();
_isolate = v8::Isolate::New(createParams);
v8::Locker locker{_isolate}; v8::Locker locker{_isolate};
@ -152,9 +153,9 @@ void V8ShellFeature::unprepare() {
v8::Locker locker{_isolate}; v8::Locker locker{_isolate};
v8::Isolate::Scope isolate_scope{_isolate}; v8::Isolate::Scope isolate_scope{_isolate};
TRI_v8_global_t* v8g = TRI_v8_global_t* v8g = static_cast<TRI_v8_global_t*>(
static_cast<TRI_v8_global_t*>(_isolate->GetData(V8DataSlot)); _isolate->GetData(arangodb::V8PlatformFeature::V8_DATA_SLOT));
_isolate->SetData(V8DataSlot, nullptr); _isolate->SetData(arangodb::V8PlatformFeature::V8_DATA_SLOT, nullptr);
delete v8g; delete v8g;
@ -401,9 +402,13 @@ int V8ShellFeature::runShell(std::vector<std::string> const& positionals) {
_console->flushLog(); _console->flushLog();
// gc // gc
if (++nrCommands >= _gcInterval) { if (++nrCommands >= _gcInterval ||
V8PlatformFeature::isOutOfMemory(_isolate)) {
nrCommands = 0; nrCommands = 0;
TRI_RunGarbageCollectionV8(_isolate, 500.0); TRI_RunGarbageCollectionV8(_isolate, 500.0);
// needs to be reset after the garbage collection
V8PlatformFeature::resetOutOfMemory(_isolate);
} }
} }

View File

@ -22,15 +22,17 @@
#include "ApplicationFeatures/V8PlatformFeature.h" #include "ApplicationFeatures/V8PlatformFeature.h"
#include "Basics/StringUtils.h"
#include "Logger/Logger.h" #include "Logger/Logger.h"
#include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/ProgramOptions.h"
#include "ProgramOptions/Section.h" #include "ProgramOptions/Section.h"
using namespace arangodb; using namespace arangodb;
using namespace arangodb::basics;
using namespace arangodb::options; using namespace arangodb::options;
namespace { namespace {
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public: public:
virtual void* Allocate(size_t length) override { virtual void* Allocate(size_t length) override {
void* data = AllocateUninitialized(length); void* data = AllocateUninitialized(length);
@ -40,7 +42,7 @@ namespace {
return malloc(length); return malloc(length);
} }
virtual void Free(void* data, size_t) override { free(data); } virtual void Free(void* data, size_t) override { free(data); }
}; };
} }
V8PlatformFeature::V8PlatformFeature( V8PlatformFeature::V8PlatformFeature(
@ -56,16 +58,34 @@ void V8PlatformFeature::collectOptions(
options->addSection("javascript", "Configure the Javascript engine"); options->addSection("javascript", "Configure the Javascript engine");
options->addHiddenOption("--javascript.v8-options", "options to pass to v8", options->addHiddenOption("--javascript.v8-options", "options to pass to v8",
new StringParameter(&_v8options)); new VectorParameter<StringParameter>(&_v8Options));
options->addOption("--javascript.v8-max-heap", "maximal heap size",
new UInt64Parameter(&_v8MaxHeap));
}
void V8PlatformFeature::validateOptions(
std::shared_ptr<ProgramOptions> 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() { void V8PlatformFeature::start() {
v8::V8::InitializeICU(); v8::V8::InitializeICU();
// explicit option --javascript.v8-options used // explicit option --javascript.v8-options used
if (!_v8options.empty()) { if (!_v8CombinedOptions.empty()) {
LOG(INFO) << "using V8 options '" << _v8options << "'"; LOG_TOPIC(INFO, Logger::V8) << "using V8 options '" << _v8CombinedOptions
v8::V8::SetFlagsFromString(_v8options.c_str(), (int)_v8options.size()); << "'";
v8::V8::SetFlagsFromString(_v8CombinedOptions.c_str(),
(int)_v8CombinedOptions.size());
} }
#ifdef TRI_FORCE_ARMV6 #ifdef TRI_FORCE_ARMV6
@ -86,3 +106,69 @@ void V8PlatformFeature::unprepare() {
_platform.reset(); _platform.reset();
_allocator.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;
}

View File

@ -25,30 +25,60 @@
#include "ApplicationFeatures/ApplicationFeature.h" #include "ApplicationFeatures/ApplicationFeature.h"
#include <v8.h>
#include <libplatform/libplatform.h> #include <libplatform/libplatform.h>
#include <v8.h>
namespace arangodb { namespace arangodb {
class V8PlatformFeature final class V8PlatformFeature final
: public application_features::ApplicationFeature { : public application_features::ApplicationFeature {
private:
struct IsolateData {
bool _outOfMemory = false;
size_t _heapSizeAtStart = 0;
};
public:
static IsolateData* getIsolateData(v8::Isolate* isolate) {
return reinterpret_cast<IsolateData*>(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: public:
explicit V8PlatformFeature(application_features::ApplicationServer* server); explicit V8PlatformFeature(application_features::ApplicationServer* server);
public: public:
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final; void start() override final;
void unprepare() override final; void unprepare() override final;
v8::ArrayBuffer::Allocator* arrayBufferAllocator() const {
return _allocator.get();
}
private: private:
std::string _v8options; std::vector<std::string> _v8Options;
uint64_t _v8MaxHeap = 3 * 1024;
public:
v8::Isolate* createIsolate();
private: private:
std::unique_ptr<v8::Platform> _platform; std::unique_ptr<v8::Platform> _platform;
std::unique_ptr<v8::ArrayBuffer::Allocator> _allocator; std::unique_ptr<v8::ArrayBuffer::Allocator> _allocator;
std::string _v8CombinedOptions;
std::vector<std::unique_ptr<IsolateData>> _isolateData;
}; };
} }

View File

@ -218,7 +218,7 @@ TRI_v8_global_t* TRI_CreateV8Globals(v8::Isolate* isolate) {
TRI_ASSERT(v8g == nullptr); TRI_ASSERT(v8g == nullptr);
v8g = new TRI_v8_global_t(isolate); v8g = new TRI_v8_global_t(isolate);
isolate->SetData(V8DataSlot, v8g); isolate->SetData(arangodb::V8PlatformFeature::V8_DATA_SLOT, v8g);
return v8g; return v8g;
} }

View File

@ -26,12 +26,10 @@
#include "Basics/Common.h" #include "Basics/Common.h"
#include <v8.h> #include "ApplicationFeatures/V8PlatformFeature.h"
struct TRI_vocbase_t; struct TRI_vocbase_t;
static const uint32_t V8DataSlot = 0;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief shortcut for fetching the isolate from the thread context /// @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) \ #define TRI_V8_TRY_CATCH_BEGIN(isolateVar) \
auto isolateVar = args.GetIsolate(); \ auto isolateVar = args.GetIsolate(); \
try { try {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief macro to terminate a try-catch sequence for V8 callbacks /// @brief macro to terminate a try-catch sequence for V8 callbacks
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -136,8 +133,8 @@ static const uint32_t V8DataSlot = 0;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#define TRI_V8_CURRENT_GLOBALS_AND_SCOPE \ #define TRI_V8_CURRENT_GLOBALS_AND_SCOPE \
TRI_v8_global_t* v8g = \ TRI_v8_global_t* v8g = static_cast<TRI_v8_global_t*>( \
static_cast<TRI_v8_global_t*>(isolate->GetData(V8DataSlot)); \ isolate->GetData(arangodb::V8PlatformFeature::V8_DATA_SLOT)); \
v8::HandleScope scope(isolate); \ v8::HandleScope scope(isolate); \
do { \ do { \
} while (0) } while (0)
@ -404,12 +401,12 @@ static const uint32_t V8DataSlot = 0;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#define TRI_GET_GLOBALS() \ #define TRI_GET_GLOBALS() \
TRI_v8_global_t* v8g = \ TRI_v8_global_t* v8g = static_cast<TRI_v8_global_t*>( \
static_cast<TRI_v8_global_t*>(isolate->GetData(V8DataSlot)) isolate->GetData(arangodb::V8PlatformFeature::V8_DATA_SLOT))
#define TRI_GET_GLOBALS2(isolate) \ #define TRI_GET_GLOBALS2(isolate) \
TRI_v8_global_t* v8g = \ TRI_v8_global_t* v8g = static_cast<TRI_v8_global_t*>( \
static_cast<TRI_v8_global_t*>(isolate->GetData(V8DataSlot)) isolate->GetData(arangodb::V8PlatformFeature::V8_DATA_SLOT))
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief fetch a string-member from the global into the local scope of the /// @brief fetch a string-member from the global into the local scope of the

View File

@ -22,14 +22,16 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "v8-vpack.h" #include "v8-vpack.h"
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
#include "ApplicationFeatures/V8PlatformFeature.h"
#include "Basics/Exceptions.h" #include "Basics/Exceptions.h"
#include "Basics/StringRef.h" #include "Basics/StringRef.h"
#include "Basics/VelocyPackHelper.h" #include "Basics/VelocyPackHelper.h"
#include "V8/v8-utils.h" #include "V8/v8-utils.h"
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
using VelocyPackHelper = arangodb::basics::VelocyPackHelper; using VelocyPackHelper = arangodb::basics::VelocyPackHelper;
/// @brief maximum object nesting depth /// @brief maximum object nesting depth
@ -74,7 +76,8 @@ static v8::Handle<v8::Value> ObjectVPackObject(v8::Isolate* isolate,
if (k.isString()) { if (k.isString()) {
// regular attribute // regular attribute
char const* p = k.getString(l); 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 { } else {
// optimized code path for translated system attributes // optimized code path for translated system attributes
VPackSlice v = VPackSlice(k.begin() + 1); VPackSlice v = VPackSlice(k.begin() + 1);
@ -86,31 +89,41 @@ static v8::Handle<v8::Value> ObjectVPackObject(v8::Isolate* isolate,
sub = TRI_VPackToV8(isolate, v, options, &slice); sub = TRI_VPackToV8(isolate, v, options, &slice);
} }
uint8_t which = static_cast<uint8_t>(k.getUInt()) + VelocyPackHelper::AttributeBase; uint8_t which =
static_cast<uint8_t>(k.getUInt()) + VelocyPackHelper::AttributeBase;
switch (which) { switch (which) {
case VelocyPackHelper::KeyAttribute: { case VelocyPackHelper::KeyAttribute: {
object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_KeyKey), sub); object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_KeyKey),
sub);
break; break;
} }
case VelocyPackHelper::RevAttribute: { case VelocyPackHelper::RevAttribute: {
object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_RevKey), sub); object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_RevKey),
sub);
break; break;
} }
case VelocyPackHelper::IdAttribute: { case VelocyPackHelper::IdAttribute: {
object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_IdKey), sub); object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_IdKey),
sub);
break; break;
} }
case VelocyPackHelper::FromAttribute: { case VelocyPackHelper::FromAttribute: {
object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_FromKey), sub); object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_FromKey),
sub);
break; break;
} }
case VelocyPackHelper::ToAttribute: { case VelocyPackHelper::ToAttribute: {
object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_ToKey), sub); object->ForceSet(v8::Local<v8::String>::New(isolate, v8g->_ToKey),
sub);
break; break;
} }
} }
} }
if (arangodb::V8PlatformFeature::isOutOfMemory(isolate)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
it.next(); it.next();
} }
@ -142,6 +155,9 @@ static v8::Handle<v8::Value> ObjectVPackArray(v8::Isolate* isolate,
if (!val.IsEmpty()) { if (!val.IsEmpty()) {
object->Set(j++, val); object->Set(j++, val);
} }
if (arangodb::V8PlatformFeature::isOutOfMemory(isolate)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
it.next(); it.next();
} }
@ -166,7 +182,8 @@ v8::Handle<v8::Value> TRI_VPackToV8(v8::Isolate* isolate,
case VPackValueType::Double: { case VPackValueType::Double: {
// convert NaN, +inf & -inf to null // convert NaN, +inf & -inf to null
double value = slice.getDouble(); 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::Null(isolate);
} }
return v8::Number::New(isolate, slice.getDouble()); return v8::Number::New(isolate, slice.getDouble());
@ -179,7 +196,8 @@ v8::Handle<v8::Value> TRI_VPackToV8(v8::Isolate* isolate,
} }
if (value >= 0 && value <= 4294967295LL) { if (value >= 0 && value <= 4294967295LL) {
// value is within bounds of a uint32_t // value is within bounds of a uint32_t
return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(value)); return v8::Integer::NewFromUnsigned(isolate,
static_cast<uint32_t>(value));
} }
// must use double to avoid truncation // must use double to avoid truncation
return v8::Number::New(isolate, static_cast<double>(slice.getInt())); return v8::Number::New(isolate, static_cast<double>(slice.getInt()));
@ -188,7 +206,8 @@ v8::Handle<v8::Value> TRI_VPackToV8(v8::Isolate* isolate,
uint64_t value = slice.getUInt(); uint64_t value = slice.getUInt();
if (value <= 4294967295ULL) { if (value <= 4294967295ULL) {
// value is within bounds of a uint32_t // value is within bounds of a uint32_t
return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(value)); return v8::Integer::NewFromUnsigned(isolate,
static_cast<uint32_t>(value));
} }
// must use double to avoid truncation // must use double to avoid truncation
return v8::Number::New(isolate, static_cast<double>(slice.getUInt())); return v8::Number::New(isolate, static_cast<double>(slice.getUInt()));
@ -207,10 +226,12 @@ v8::Handle<v8::Value> TRI_VPackToV8(v8::Isolate* isolate,
} }
case VPackValueType::External: { case VPackValueType::External: {
// resolve external // resolve external
return TRI_VPackToV8(isolate, VPackSlice(slice.getExternal()), options, base); return TRI_VPackToV8(isolate, VPackSlice(slice.getExternal()), options,
base);
} }
case VPackValueType::Custom: { 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, THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
"Could not extract custom attribute."); "Could not extract custom attribute.");
} }
@ -219,9 +240,7 @@ v8::Handle<v8::Value> TRI_VPackToV8(v8::Isolate* isolate,
return TRI_V8_STD_STRING(id); return TRI_V8_STD_STRING(id);
} }
case VPackValueType::None: case VPackValueType::None:
default: { default: { return v8::Undefined(isolate); }
return v8::Undefined(isolate);
}
} }
} }
@ -244,8 +263,9 @@ struct BuilderContext {
/// @brief adds a VPackValue to either an array or an object /// @brief adds a VPackValue to either an array or an object
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
template<typename T, bool inObject> template <typename T, bool inObject>
static inline void AddValue(BuilderContext& context, arangodb::StringRef const& attributeName, static inline void AddValue(BuilderContext& context,
arangodb::StringRef const& attributeName,
T const& value) { T const& value) {
if (inObject) { if (inObject) {
context.builder.add(attributeName.begin(), attributeName.size(), value); context.builder.add(attributeName.begin(), attributeName.size(), value);
@ -262,7 +282,6 @@ template <bool performAllChecks, bool inObject>
static int V8ToVPack(BuilderContext& context, static int V8ToVPack(BuilderContext& context,
v8::Handle<v8::Value> const parameter, v8::Handle<v8::Value> const parameter,
arangodb::StringRef const& attributeName) { arangodb::StringRef const& attributeName) {
if (parameter->IsNull() || parameter->IsUndefined()) { if (parameter->IsNull() || parameter->IsUndefined()) {
AddValue<VPackValue, inObject>(context, attributeName, AddValue<VPackValue, inObject>(context, attributeName,
VPackValue(VPackValueType::Null)); VPackValue(VPackValueType::Null));
@ -283,8 +302,8 @@ static int V8ToVPack(BuilderContext& context,
} }
if (parameter->IsUint32()) { if (parameter->IsUint32()) {
AddValue<VPackValue, inObject>(context, attributeName, AddValue<VPackValue, inObject>(
VPackValue(parameter->ToUint32()->Value())); context, attributeName, VPackValue(parameter->ToUint32()->Value()));
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
@ -300,7 +319,9 @@ static int V8ToVPack(BuilderContext& context,
return TRI_ERROR_OUT_OF_MEMORY; return TRI_ERROR_OUT_OF_MEMORY;
} }
AddValue<VPackValuePair, inObject>(context, attributeName, VPackValuePair(*str, str.length(), VPackValueType::String)); AddValue<VPackValuePair, inObject>(
context, attributeName,
VPackValuePair(*str, str.length(), VPackValueType::String));
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
@ -323,7 +344,8 @@ static int V8ToVPack(BuilderContext& context,
return TRI_ERROR_BAD_PARAMETER; return TRI_ERROR_BAD_PARAMETER;
} }
int res = V8ToVPack<performAllChecks, false>(context, value, arangodb::StringRef()); int res = V8ToVPack<performAllChecks, false>(context, value,
arangodb::StringRef());
--context.level; --context.level;
@ -341,16 +363,18 @@ static int V8ToVPack(BuilderContext& context,
if (parameter->IsObject()) { if (parameter->IsObject()) {
if (performAllChecks) { if (performAllChecks) {
if (parameter->IsBooleanObject()) { if (parameter->IsBooleanObject()) {
AddValue<VPackValue, inObject>(context, attributeName, AddValue<VPackValue, inObject>(
context, attributeName,
VPackValue(v8::Handle<v8::BooleanObject>::Cast(parameter) VPackValue(v8::Handle<v8::BooleanObject>::Cast(parameter)
->BooleanValue())); ->BooleanValue()));
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
if (parameter->IsNumberObject()) { if (parameter->IsNumberObject()) {
AddValue<VPackValue, inObject>(context, attributeName, AddValue<VPackValue, inObject>(
VPackValue(v8::Handle<v8::NumberObject>::Cast(parameter) context, attributeName,
->NumberValue())); VPackValue(
v8::Handle<v8::NumberObject>::Cast(parameter)->NumberValue()));
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
@ -361,7 +385,9 @@ static int V8ToVPack(BuilderContext& context,
return TRI_ERROR_OUT_OF_MEMORY; return TRI_ERROR_OUT_OF_MEMORY;
} }
AddValue<VPackValuePair, inObject>(context, attributeName, VPackValuePair(*str, str.length(), VPackValueType::String)); AddValue<VPackValuePair, inObject>(
context, attributeName,
VPackValuePair(*str, str.length(), VPackValueType::String));
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
@ -379,7 +405,8 @@ static int V8ToVPack(BuilderContext& context,
// call it if yes // call it if yes
v8::Handle<v8::Value> func = o->Get(context.toJsonKey); v8::Handle<v8::Value> func = o->Get(context.toJsonKey);
if (func->IsFunction()) { if (func->IsFunction()) {
v8::Handle<v8::Function> toJson = v8::Handle<v8::Function>::Cast(func); v8::Handle<v8::Function> toJson =
v8::Handle<v8::Function>::Cast(func);
v8::Handle<v8::Value> args; v8::Handle<v8::Value> args;
v8::Handle<v8::Value> converted = toJson->Call(o, 0, &args); v8::Handle<v8::Value> 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 // this passes ownership for the utf8 string to the JSON object
AddValue<VPackValuePair, inObject>(context, attributeName, VPackValuePair(*str, str.length(), VPackValueType::String)); AddValue<VPackValuePair, inObject>(
context, attributeName,
VPackValuePair(*str, str.length(), VPackValueType::String));
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
} }
@ -428,7 +457,8 @@ static int V8ToVPack(BuilderContext& context,
return TRI_ERROR_BAD_PARAMETER; return TRI_ERROR_BAD_PARAMETER;
} }
int res = V8ToVPack<performAllChecks, true>(context, value, arangodb::StringRef(*str, str.length())); int res = V8ToVPack<performAllChecks, true>(
context, value, arangodb::StringRef(*str, str.length()));
--context.level; --context.level;
@ -466,10 +496,10 @@ int TRI_V8ToVPack(v8::Isolate* isolate, VPackBuilder& builder,
/// does not contain types such as Function, Date or RegExp /// 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<v8::Value> const value) { v8::Handle<v8::Value> const value) {
// a HandleScope must have been created by the caller already // a HandleScope must have been created by the caller already
BuilderContext context(isolate, builder, false); BuilderContext context(isolate, builder, false);
return V8ToVPack<false, false>(context, value, arangodb::StringRef()); return V8ToVPack<false, false>(context, value, arangodb::StringRef());
} }