//////////////////////////////////////////////////////////////////////////////// /// @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 "Basics/Dictionary.h" #include "Basics/StringUtils.h" #include "BasicsC/conversions.h" #include "BasicsC/csv.h" #include "BasicsC/files.h" #include "BasicsC/logging.h" #include "BasicsC/process-utils.h" #include "BasicsC/string-buffer.h" #include "BasicsC/strings.h" #include "Rest/SslInterface.h" #include "V8/v8-conv.h" using namespace std; using namespace triagens::basics; using namespace triagens::rest; // ----------------------------------------------------------------------------- // --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(TRI_UNKNOWN_MEM_ZONE, key); TRI_Free(TRI_UNKNOWN_MEM_ZONE, 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_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, key.c_str()); if (ckey == NULL) { return scope.Close(v8::ThrowException(v8::String::New("out-of-memory"))); } // 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(TRI_UNKNOWN_MEM_ZONE, filename); if (content == 0) { LOG_TRACE("cannot loaded java script file '%s': %s", filename, TRI_last_error()); return false; } if (execute) { char* contentWrapper = TRI_Concatenate3String("(function() { ", content, "/* end-of-file */ })()"); TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content); content = contentWrapper; } v8::Handle name = v8::String::New(filename); v8::Handle source = v8::String::New(content); TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, 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 JavaScript 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); ok = LoadJavaScriptFile(context, full, execute); TRI_FreeString(TRI_CORE_MEM_ZONE, 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(