//////////////////////////////////////////////////////////////////////////////// /// @brief V8 utility functions /// /// @file /// /// DISCLAIMER /// /// Copyright 2004-2012 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 triAGENS GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Copyright 2011-2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "v8-utils.h" #include #include #include #include #include #include #include #include #include #include #include #include "VocBase/query-base.h" #include "V8/v8-conv.h" using namespace std; using namespace triagens::basics; // ----------------------------------------------------------------------------- // --SECTION-- EXECUTION CONTEXT // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- public types // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Utils /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief execution context //////////////////////////////////////////////////////////////////////////////// typedef struct js_exec_context_s { v8::Persistent _func; v8::Persistent _arguments; } js_exec_context_t; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- constructors and destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Utils /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief creates a new execution context //////////////////////////////////////////////////////////////////////////////// TRI_js_exec_context_t TRI_CreateExecutionContext (char const* script) { js_exec_context_t* ctx; // execute script inside the context v8::Handle compiled = v8::Script::Compile(v8::String::New(script), v8::String::New("--script--")); // compilation failed, print errors that happened during compilation if (compiled.IsEmpty()) { return 0; } // compute the function v8::Handle val = compiled->Run(); if (val.IsEmpty()) { return 0; } ctx = new js_exec_context_t; ctx->_func = v8::Persistent::New(v8::Handle::Cast(val)); ctx->_arguments = v8::Persistent::New(v8::Object::New()); // return the handle return (TRI_js_exec_context_t) ctx; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees an new execution context //////////////////////////////////////////////////////////////////////////////// void TRI_FreeExecutionContext (TRI_js_exec_context_t context) { js_exec_context_t* ctx; ctx = (js_exec_context_t*) context; ctx->_func.Dispose(); ctx->_func.Clear(); ctx->_arguments.Dispose(); ctx->_arguments.Clear(); delete ctx; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Utils /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief sets an json array //////////////////////////////////////////////////////////////////////////////// bool TRI_DefineJsonArrayExecutionContext (TRI_js_exec_context_t context, TRI_json_t* json) { js_exec_context_t* ctx; v8::Handle result; ctx = (js_exec_context_t*) context; assert(json->_type == TRI_JSON_ARRAY); size_t n = json->_value._objects._length; for (size_t i = 0; i < n; i += 2) { TRI_json_t* key = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i); if (key->_type != TRI_JSON_STRING) { continue; } TRI_json_t* j = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i + 1); v8::Handle val = TRI_ObjectJson(j); ctx->_arguments->Set(v8::String::New(key->_value._string.data), val); } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief defines documents in a join/where - DEPRECATED //////////////////////////////////////////////////////////////////////////////// bool TRI_DefineWhereExecutionContextX (TRI_js_exec_context_t context, const TRI_select_join_t* join, const size_t level, const bool isJoin) { js_exec_context_t* ctx; TRI_doc_mptr_t* document; ctx = (js_exec_context_t*) context; for (size_t i = 0; i <= level; i++) { TRI_join_part_t* part = (TRI_join_part_t*) join->_parts._buffer[i]; if (part->_type != JOIN_TYPE_LIST || (isJoin && (level == i))) { // part is a single-document container document = (TRI_doc_mptr_t*) part->_singleDocument; if (!document) { ctx->_arguments->Set(v8::String::New(part->_alias), v8::Null()); } else { v8::Handle result; bool ok = TRI_ObjectDocumentPointer(part->_collection->_collection, document, &result); if (!ok) { return false; } ctx->_arguments->Set(v8::String::New(part->_alias), result); } if (part->_extraData._size) { // make extra values available ctx->_arguments->Set(v8::String::New(part->_extraData._alias), v8::Number::New(*((double*) part->_extraData._singleValue))); } } else { // part is a multi-document container v8::Handle array = v8::Array::New(); size_t pos = 0; for (size_t n = 0; n < part->_listDocuments._length; n++) { document = (TRI_doc_mptr_t*) part->_listDocuments._buffer[n]; if (document) { v8::Handle result; bool ok = TRI_ObjectDocumentPointer(part->_collection->_collection, document, &result); if (!ok) { return false; } array->Set(pos++, result); } } ctx->_arguments->Set(v8::String::New(part->_alias), array); if (part->_extraData._size) { // make extra values available v8::Handle array = v8::Array::New(); uint32_t pos = 0; for (size_t n = 0; n < part->_extraData._listValues._length; n++) { double* data = (double*) part->_extraData._listValues._buffer[n]; if (data) { v8::Handle result; array->Set(pos++, v8::Number::New(*data)); } } ctx->_arguments->Set(v8::String::New(part->_extraData._alias), array); } } } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief defines documents in a join/where //////////////////////////////////////////////////////////////////////////////// bool TRI_DefineWhereExecutionContext (TRI_query_instance_t* const instance, TRI_js_exec_context_t context, const size_t level, const bool isJoin) { js_exec_context_t* ctx; TRI_doc_mptr_t* document; ctx = (js_exec_context_t*) context; for (size_t i = 0; i <= level; i++) { TRI_join_part_t* part = (TRI_join_part_t*) instance->_join._buffer[i]; assert(part); if ((isJoin && !part->_mustMaterialize._join) || (!isJoin && !part->_mustMaterialize._where)) { // no need to materialize query part continue; } if (part->_type != JOIN_TYPE_LIST || (isJoin && (level == i))) { // part is a single-document container document = (TRI_doc_mptr_t*) part->_singleDocument; if (!document) { ctx->_arguments->Set(v8::String::New(part->_alias), v8::Null()); } else { v8::Handle result; bool ok = TRI_ObjectDocumentPointer(part->_collection->_collection, document, &result); if (!ok) { return false; } ctx->_arguments->Set(v8::String::New(part->_alias), result); } if (part->_extraData._size) { // make extra values available ctx->_arguments->Set(v8::String::New(part->_extraData._alias), v8::Number::New(*((double*) part->_extraData._singleValue))); } } else { // part is a multi-document container v8::Handle array = v8::Array::New(); uint32_t pos = 0; for (size_t n = 0; n < part->_listDocuments._length; n++) { document = (TRI_doc_mptr_t*) part->_listDocuments._buffer[n]; if (document) { v8::Handle result; bool ok = TRI_ObjectDocumentPointer(part->_collection->_collection, document, &result); if (!ok) { return false; } array->Set(pos++, result); } } ctx->_arguments->Set(v8::String::New(part->_alias), array); if (part->_extraData._size) { // make extra values available v8::Handle array = v8::Array::New(); size_t pos = 0; for (size_t n = 0; n < part->_extraData._listValues._length; n++) { double* data = (double*) part->_extraData._listValues._buffer[n]; if (data) { v8::Handle result; array->Set(pos++, v8::Number::New(*data)); } } ctx->_arguments->Set(v8::String::New(part->_extraData._alias), array); } } } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief defines a document composed of multiple elements //////////////////////////////////////////////////////////////////////////////// bool TRI_DefineSelectExecutionContext (TRI_js_exec_context_t context, TRI_rc_result_t* resultState) { js_exec_context_t* ctx; TRI_sr_documents_t document; TRI_sr_documents_t* docPtr; TRI_select_size_t* numPtr; TRI_select_size_t num; TRI_select_result_t* result; assert(resultState); numPtr = (TRI_select_size_t*) resultState->_dataPtr; if (!numPtr) { return false; } result = resultState->_selectResult; ctx = (js_exec_context_t*) context; for (size_t i = 0; i < result->_dataParts->_length; i++) { TRI_select_datapart_t* part = (TRI_select_datapart_t*) result->_dataParts->_buffer[i]; num = *numPtr++; docPtr = (TRI_sr_documents_t*) numPtr; if (part->_type == RESULT_PART_DOCUMENT_SINGLE) { document = (TRI_sr_documents_t) *docPtr++; if (!document) { ctx->_arguments->Set(v8::String::New(part->_alias), v8::Null()); } else { v8::Handle result; TRI_doc_mptr_t masterPointer; TRI_MarkerMasterPointer(document, &masterPointer); bool ok = TRI_ObjectDocumentPointer(part->_collection, &masterPointer, &result); if (!ok) { return false; } ctx->_arguments->Set(v8::String::New(part->_alias), result); } } else if (part->_type == RESULT_PART_DOCUMENT_MULTI) { // part is a multi-document container v8::Handle array = v8::Array::New(); size_t pos = 0; for (size_t i = 0; i < num; i++) { document = (TRI_sr_documents_t) *docPtr++; if (document) { v8::Handle result; TRI_doc_mptr_t masterPointer; TRI_MarkerMasterPointer(document, &masterPointer); bool ok = TRI_ObjectDocumentPointer(part->_collection, &masterPointer, &result); if (!ok) { return false; } array->Set(pos++, result); } } ctx->_arguments->Set(v8::String::New(part->_alias), array); } else if (part->_type == RESULT_PART_VALUE_SINGLE) { void* value = (void*) docPtr; ctx->_arguments->Set(v8::String::New(part->_alias), v8::Number::New(*(double*) value)); docPtr = (TRI_sr_documents_t*) ((uint8_t*) docPtr + part->_extraDataSize); } else if (part->_type == RESULT_PART_VALUE_MULTI) { v8::Handle array = v8::Array::New(); size_t pos = 0; for (size_t i = 0; i < num; i++) { void* value = (void*) docPtr; array->Set(pos++, v8::Number::New(*(double*) value)); docPtr = (TRI_sr_documents_t*) ((uint8_t*) docPtr + part->_extraDataSize); } ctx->_arguments->Set(v8::String::New(part->_alias), array); } numPtr = (TRI_select_size_t*) docPtr; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a v8 object from a document list //////////////////////////////////////////////////////////////////////////////// static bool MakeObject (TRI_select_result_t* result, TRI_sr_documents_t* docPtr, void* storage) { TRI_sr_documents_t document; TRI_select_size_t* numPtr; TRI_select_size_t num; v8::Handle obj = v8::Object::New(); numPtr = (TRI_select_size_t*) docPtr; for (size_t i = 0; i < result->_dataParts->_length; i++) { TRI_select_datapart_t* part = (TRI_select_datapart_t*) result->_dataParts->_buffer[i]; assert(part); if (!part->_mustMaterialize._order) { // no need to materialize continue; } num = *numPtr++; docPtr = (TRI_sr_documents_t*) numPtr; if (part->_type == RESULT_PART_DOCUMENT_SINGLE) { document = (TRI_sr_documents_t) *docPtr++; if (!document) { obj->Set(v8::String::New(part->_alias), v8::Null()); } else { v8::Handle result; TRI_doc_mptr_t masterPointer; TRI_MarkerMasterPointer(document, &masterPointer); bool ok = TRI_ObjectDocumentPointer(part->_collection, &masterPointer, &result); if (!ok) { return false; } obj->Set(v8::String::New(part->_alias), result); } } else if (part->_type == RESULT_PART_DOCUMENT_MULTI) { // part is a multi-document container v8::Handle array = v8::Array::New(); uint32_t pos = 0; for (size_t i = 0; i < num; i++) { document = (TRI_sr_documents_t) *docPtr++; if (document) { v8::Handle result; TRI_doc_mptr_t masterPointer; TRI_MarkerMasterPointer(document, &masterPointer); bool ok = TRI_ObjectDocumentPointer(part->_collection, &masterPointer, &result); if (!ok) { return false; } array->Set(pos++, result); } } obj->Set(v8::String::New(part->_alias), array); } else if (part->_type == RESULT_PART_VALUE_SINGLE) { void* value = (void*) docPtr; obj->Set(v8::String::New(part->_alias), v8::Number::New(*(double*) value)); docPtr = (TRI_sr_documents_t*) ((uint8_t*) docPtr + part->_extraDataSize); } else if (part->_type == RESULT_PART_VALUE_MULTI) { v8::Handle array = v8::Array::New(); uint32_t pos = 0; for (size_t i = 0; i < num; i++) { void* value = (void*) docPtr; array->Set(pos++, v8::Number::New(*(double*) value)); docPtr = (TRI_sr_documents_t*) ((uint8_t*) docPtr + part->_extraDataSize); } obj->Set(v8::String::New(part->_alias), array); } numPtr = (TRI_select_size_t*) docPtr; } * (v8::Handle*) storage = obj; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief defines an execution context with two documents for comparisons //////////////////////////////////////////////////////////////////////////////// bool TRI_DefineCompareExecutionContext (TRI_js_exec_context_t context, TRI_select_result_t* result, TRI_sr_documents_t* left, TRI_sr_documents_t* right) { js_exec_context_t* ctx; ctx = (js_exec_context_t*) context; assert(ctx); v8::Handle leftValue; v8::Handle rightValue; MakeObject(result, left, &leftValue); MakeObject(result, right, &rightValue); ctx->_arguments->Set(v8::String::New("l"), leftValue); ctx->_arguments->Set(v8::String::New("r"), rightValue); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes an execution context //////////////////////////////////////////////////////////////////////////////// bool TRI_ExecuteExecutionContext (TRI_js_exec_context_t context, void* storage) { js_exec_context_t* ctx; ctx = (js_exec_context_t*) context; // convert back into a handle v8::Persistent func = ctx->_func; // and execute the function v8::Handle args[] = { ctx->_arguments }; v8::Handle result = func->Call(func, 1, args); if (result.IsEmpty()) { return false; } * (v8::Handle*) storage = result; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes an execution context for a condition - DEPRECATED //////////////////////////////////////////////////////////////////////////////// bool TRI_ExecuteConditionExecutionContextX (TRI_js_exec_context_t context, bool* r) { js_exec_context_t* ctx; ctx = (js_exec_context_t*) context; // convert back into a handle v8::Persistent func = ctx->_func; // and execute the function v8::Handle args[] = { ctx->_arguments }; v8::Handle result = func->Call(func, 1, args); if (result.IsEmpty()) { return false; } *r = TRI_ObjectToBoolean(result); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes an execution context for a condition //////////////////////////////////////////////////////////////////////////////// bool TRI_ExecuteConditionExecutionContext (TRI_query_instance_t* const instance, TRI_js_exec_context_t context, bool* r) { js_exec_context_t* ctx; ctx = (js_exec_context_t*) context; // convert back into a handle v8::Persistent func = ctx->_func; // and execute the function v8::Handle args[] = { ctx->_arguments }; v8::Handle result = func->Call(func, 1, args); if (result.IsEmpty()) { return false; } *r = TRI_ObjectToBoolean(result); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes an execution context for ref access //////////////////////////////////////////////////////////////////////////////// bool TRI_ExecuteRefExecutionContext (TRI_js_exec_context_t context, TRI_json_t* r) { js_exec_context_t* ctx; ctx = (js_exec_context_t*) context; // convert back into a handle v8::Persistent func = ctx->_func; // and execute the function v8::Handle args[] = { ctx->_arguments }; v8::Handle result = func->Call(func, 1, args); if (result.IsEmpty() || !result->IsArray()) { return false; } v8::Handle obj = result->ToObject(); uint32_t position = 0; while (true) { if (!obj->Has(position)) { break; } v8::Handle parameter = obj->Get(position++); if (parameter->IsNumber()) { v8::Handle numberParameter = parameter->ToNumber(); TRI_PushBack2ListJson(r, TRI_CreateNumberJson(numberParameter->Value())); } else if (parameter->IsString() ) { v8::Handle stringParameter= parameter->ToString(); v8::String::Utf8Value str(stringParameter); TRI_PushBack2ListJson(r, TRI_CreateStringCopyJson(*str)); } else { continue; } } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes an execution context for order by //////////////////////////////////////////////////////////////////////////////// bool TRI_ExecuteOrderExecutionContext (TRI_js_exec_context_t context, int* r) { v8::TryCatch tryCatch; js_exec_context_t* ctx; ctx = (js_exec_context_t*) context; // convert back into a handle v8::Persistent func = ctx->_func; // and execute the function v8::Handle args[] = { ctx->_arguments }; v8::Handle result = func->Call(func, 1, args); if (result.IsEmpty()) { return false; } *r = (int) TRI_ObjectToDouble(result); return true; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- WEAK DICTIONARY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private constants // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Utils /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief wrapped class for TRI_vocbase_t //////////////////////////////////////////////////////////////////////////////// static int32_t const WRP_WEAK_DIRECTORY_TYPE = 1000; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- private types // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Utils /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief dictionary / key pair //////////////////////////////////////////////////////////////////////////////// typedef struct { void* _dictionary; char* _key; } wd_key_pair_t; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Utils /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief weak dictionary callback //////////////////////////////////////////////////////////////////////////////// static void WeakDictionaryCallback (v8::Persistent object, void* parameter) { typedef Dictionary< v8::Persistent* > WD; WD* dictionary; char* key; dictionary = (WD*) ((wd_key_pair_t*) parameter)->_dictionary; key = ((wd_key_pair_t*) parameter)->_key; LOG_TRACE("weak-callback for dictionary called"); // dispose and clear the persistent handle WD::KeyValue const* kv = dictionary->lookup(key); if (kv != 0) { const_cast(kv)->_value->Dispose(); const_cast(kv)->_value->Clear(); delete const_cast(kv)->_value; dictionary->erase(key); } TRI_FreeString(key); TRI_Free(parameter); } //////////////////////////////////////////////////////////////////////////////// /// @brief invocation callback //////////////////////////////////////////////////////////////////////////////// static v8::Handle WeakDictionaryInvocationCallback (v8::Arguments const& args) { typedef Dictionary< v8::Persistent* > WD; static uint64_t MIN_SIZE = 100; v8::Handle self = args.Holder(); if (self->InternalFieldCount() <= 1) { return v8::ThrowException(v8::String::New("corrupted weak dictionary")); } WD* dictionary = new WD(MIN_SIZE); v8::Handle external = v8::Persistent::New(v8::External::New(dictionary)); self->SetInternalField(SLOT_CLASS, external); self->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(WRP_WEAK_DIRECTORY_TYPE)); return self; } //////////////////////////////////////////////////////////////////////////////// /// @brief checks if a property is present //////////////////////////////////////////////////////////////////////////////// static v8::Handle PropertyQueryWeakDictionary (v8::Local name, const v8::AccessorInfo& info) { typedef Dictionary< v8::Persistent* > WD; v8::HandleScope scope; // sanity check v8::Handle self = info.Holder(); // get the dictionary WD* dictionary = TRI_UnwrapClass(self, WRP_WEAK_DIRECTORY_TYPE); if (dictionary == 0) { return scope.Close(v8::Handle()); } // convert the JavaScript string to a string string key = TRI_ObjectToString(name); if (key == "") { return scope.Close(v8::Handle()); } // check the dictionary WD::KeyValue const* kv = dictionary->lookup(key.c_str()); if (kv == 0) { return scope.Close(v8::Handle()); } return scope.Close(v8::Handle(v8::Integer::New(v8::None))); } //////////////////////////////////////////////////////////////////////////////// /// @brief keys of a dictionary //////////////////////////////////////////////////////////////////////////////// static v8::Handle KeysOfWeakDictionary (const v8::AccessorInfo& info) { typedef Dictionary< v8::Persistent* > WD; v8::HandleScope scope; v8::Handle result = v8::Array::New(); // sanity check v8::Handle self = info.Holder(); // get the dictionary WD* dictionary = TRI_UnwrapClass(self, WRP_WEAK_DIRECTORY_TYPE); if (dictionary == 0) { return scope.Close(result); } // check the dictionary WD::KeyValue const* begin; WD::KeyValue const* end; WD::KeyValue const* ptr; dictionary->range(begin, end); size_t count = 0; for (ptr = begin; ptr < end; ++ptr) { if (ptr->_key != 0) { result->Set(count++, v8::String::New(ptr->_key)); } } return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief gets an entry //////////////////////////////////////////////////////////////////////////////// static v8::Handle MapGetWeakDictionary (v8::Local name, const v8::AccessorInfo& info) { typedef Dictionary< v8::Persistent* > WD; v8::HandleScope scope; // sanity check v8::Handle self = info.Holder(); // get the dictionary WD* dictionary = TRI_UnwrapClass(self, WRP_WEAK_DIRECTORY_TYPE); if (dictionary == 0) { return scope.Close(v8::ThrowException(v8::String::New("corrupted weak dictionary"))); } // convert the JavaScript string to a string string key = TRI_ObjectToString(name); if (key == "") { return scope.Close(v8::Undefined()); } // check the dictionary WD::KeyValue const* kv = dictionary->lookup(key.c_str()); if (kv == 0) { return scope.Close(v8::Undefined()); } return scope.Close(*kv->_value); } //////////////////////////////////////////////////////////////////////////////// /// @brief gets an entry //////////////////////////////////////////////////////////////////////////////// static v8::Handle MapSetWeakDictionary (v8::Local name, v8::Local value, const v8::AccessorInfo& info) { typedef Dictionary< v8::Persistent* > WD; v8::HandleScope scope; // sanity check v8::Handle self = info.Holder(); // get the dictionary WD* dictionary = TRI_UnwrapClass(self, WRP_WEAK_DIRECTORY_TYPE); if (dictionary == 0) { return scope.Close(v8::ThrowException(v8::String::New("corrupted weak dictionary"))); } // convert the JavaScript string to a string string key = TRI_ObjectToString(name); if (key == "") { return scope.Close(v8::Undefined()); } char* ckey = TRI_DuplicateString(key.c_str()); // create a new weak persistent v8::Persistent* persistent = new v8::Persistent(); *persistent = v8::Persistent::New(value); // enter a value into the dictionary WD::KeyValue const* kv = dictionary->lookup(ckey); if (kv != 0) { kv->_value->Dispose(); kv->_value->Clear(); delete kv->_value; const_cast(kv)->_value = persistent; } else { dictionary->insert(ckey, persistent); } wd_key_pair_t* p = new wd_key_pair_t; p->_dictionary = dictionary; p->_key = ckey; persistent->MakeWeak(p, WeakDictionaryCallback); return scope.Close(value); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- GENERAL // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Utils /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief reads/execute a file into/in the current context //////////////////////////////////////////////////////////////////////////////// static bool LoadJavaScriptFile (v8::Handle context, char const* filename, bool execute) { v8::HandleScope handleScope; char* content = TRI_SlurpFile(filename); if (content == 0) { LOG_TRACE("cannot loaded java script file '%s': %s", filename, TRI_last_error()); return false; } if (execute) { char* contentWrapper = TRI_Concatenate5String("(function() { ", content, "/* end-of-file '", filename, "' */ })()"); TRI_FreeString(content); content = contentWrapper; } v8::Handle name = v8::String::New(filename); v8::Handle source = v8::String::New(content); TRI_FreeString(content); v8::Handle script = v8::Script::Compile(source, name); // compilation failed, print errors that happened during compilation if (script.IsEmpty()) { return false; } // execute script v8::Handle result = script->Run(); if (result.IsEmpty()) { return false; } LOG_TRACE("loaded java script file: '%s'", filename); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief reads all files from a directory into the current context //////////////////////////////////////////////////////////////////////////////// static bool LoadJavaScriptDirectory (v8::Handle context, char const* path, bool execute) { v8::HandleScope scope; TRI_vector_string_t files; bool result; regex_t re; size_t i; LOG_TRACE("loading java script directory: '%s'", path); files = TRI_FilesDirectory(path); regcomp(&re, "^(.*)\\.js$", REG_ICASE | REG_EXTENDED); result = true; for (i = 0; i < files._length; ++i) { v8::TryCatch tryCatch; bool ok; char const* filename; char* full; filename = files._buffer[i]; if (! regexec(&re, filename, 0, 0, 0) == 0) { continue; } full = TRI_Concatenate2File(path, filename); if (!full) { continue; } ok = LoadJavaScriptFile(context, full, execute); TRI_FreeString(full); result = result && ok; if (! ok) { TRI_LogV8Exception(&tryCatch); } } TRI_DestroyVectorString(&files); regfree(&re); return result; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- JS functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Utils /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief executes a script /// /// @FUN{internal.execute(@FA{script}, @FA{sandbox}, @FA{filename})} /// /// Executes the @FA{script} with the @FA{sandbox} as context. Global variables /// assigned inside the @FA{script}, will be visible in the @FA{sandbox} object /// after execution. The @FA{filename} is used for displaying error /// messages. /// /// If @FA{sandbox} is undefined, then @FN{execute} uses the current context. //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_Execute (v8::Arguments const& argv) { v8::HandleScope scope; size_t i; // extract arguments if (argv.Length() != 3) { return scope.Close(v8::ThrowException(v8::String::New("usage: execute(